This is an archive of the discontinued LLVM Phabricator instance.

[DWARF] Introduce verification for the unit header chain in .debug_info section to llvm-dwarfdump.
ClosedPublic

Authored by sgravani on Jul 7 2017, 6:35 PM.

Details

Summary

This patch adds verification checks for the unit header chain in the .debug_info section.

Specifically, for each unit in the .debug_info section, the verifier checks that:

  1. The unit length is valid (i.e. the unit can actually fit in the .debug_info section)
  2. The dwarf version of the unit is valid
  3. The address size is valid (4 or 8)
  4. The unit type (if the unit is in dwarf5) is valid
  5. The debug_abbrev_offset is valid

I'm still working on test cases, but I wanted to submit the patch here in order to get feedback.

Diff Detail

Event Timeline

sgravani created this revision.Jul 7 2017, 6:35 PM
dblaikie edited edge metadata.Jul 9 2017, 9:50 AM

Just a few drive by thoughts, but happy to leave this mostly to Adrian

include/llvm/DebugInfo/DWARF/DWARFUnit.h
250

Might be nice for consistency to have the default in the switch as well.

Also might be easier to return directly rather than having an extra variable:

switch (UnitType) {
case skeleton:
case split_compile:
  return 20;
case type:
case split-type:
  return 24;
case compile:
case partial:
default:
  return 12;
}

That said, should this switch have a default? Or is it reasonable to say "this must be called with a valid unit type, of which there are only these 6"?

include/llvm/DebugInfo/DWARF/DWARFVerifier.h
44

Why is this a member variable? It looks like it's only used within a local scope of handleDebugInfoUnitHeaderChain

lib/DebugInfo/DWARF/DWARFContext.cpp
427–429

Arguably, each unit with a valid header (that doesn't go off the end, etc) could be validated - just because one header is mangled doesn't mean the rest are (granted if it's mangled in a way that means the length is wrong and the rest of the headers from then on are probably off - then there's not much you can do with everything after the broken one, of course). Not necessary, but not impossible either. If it's going to stay this way maybe the error could be rephrased "unit contents will not be verified" (not that tehy don't need to be, or that they wouldn't benefit from being verified, etc)

lib/DebugInfo/DWARF/DWARFVerifier.cpp
50

Looks like this could never return false - since if the Offset+HeaderSize is a valid offset, then 'Offset' must be too (since it's a zero based array index - so X + N (where X and N are positive) can't be valid while X is invalid)

74

Looks like this probably only needs to be a boolean flag (& a local, as mentioned earlier) rather than a counter.

75

When does this happen? Seems like it could only happen if the debug_info section was empty, maybe? (Otherwise the previous unit would've already failed because it was too long) Perhaps it'd be better to have a special case for empty? (or maybe it's not an error, really?)

probinson added inline comments.Jul 10 2017, 1:15 PM
include/llvm/DebugInfo/DWARF/DWARFUnit.h
241

If these static methods are used only by the verifier, maybe they should just be local functions in DWARFVerifier.cpp.

lib/DebugInfo/DWARF/DWARFVerifier.cpp
39

This is assuming DWARF-32 format. It's okay to support only one format, if you have a check for DWARF-64 and explicitly reject it.

44

This could call computeDWARF5HeaderSize with an invalid unit type. That's okay if you document the contract in the declaration of that method.

51

Might want a DWARF-64 check here.

54

Improper TOCTOU. Admittedly this is far from security-sensitive, but using data before validating it is generally poor practice.

59

Again could call computeDWARF5HeaderSize with an invalid unit type.

sgravani updated this revision to Diff 105945.Jul 10 2017, 5:20 PM
sgravani marked 6 inline comments as done.

Added code that stops verification if it identifies a unit with DWARF64 format.
Addressed other reviewers' comments.

include/llvm/DebugInfo/DWARF/DWARFUnit.h
250

I think it makes more sense not to have the default. I modified this to reflect that the only valid arguments are these 6 types for dwarf5.
Thanks!

lib/DebugInfo/DWARF/DWARFContext.cpp
427–429

I agree that there's still value in verifying the contents of .debug_info, even when the unit header chain check fails. I'm changing this to have verification of the debug info section in any case. Thanks.

lib/DebugInfo/DWARF/DWARFVerifier.cpp
50

You're right. I fixed this part.

75

You're right. The only case would be if .debug_info section was empty.
I'm not sure if we should handle this as an error.
I think I would like to go with printing a warning message for this case (since we're verifying the .debug_info section and .debug_info is empty).

sgravani marked 3 inline comments as done.Jul 10 2017, 5:24 PM
sgravani added inline comments.
lib/DebugInfo/DWARF/DWARFVerifier.cpp
39

I'm stopping verification if I detect a unit with 64-bit format (without generating error).
Thanks!

friss edited edge metadata.Jul 11 2017, 6:47 PM

Some random comments:

include/llvm/DebugInfo/DWARF/DWARFUnit.h
241

Not important, but I would call this isValidUnitType().

254

Really nit-picky: This doesn't seem to 'compute' anything. Why not just getDWARF5HeaderSize()?

264

I don't think we should return 12 when hitting the default case. If every use of computeDWARF5HeaderSize() is guarded by validateUnitType(), then having the default case be llvm_unreachable makes more sense.

include/llvm/DebugInfo/DWARF/DWARFVerifier.h
39

I don't like having that state which is only used for one check at the class level. Although, the name of the struct is way too generic. 'struct UnitHeaderFlags' maybe?

lib/DebugInfo/DWARF/DWARFVerifier.cpp
47–48

I wouldn't try to get a size if the unit type is not recognized. It seems wrong. I'm not sure we should try to go any further than this when we can't figure out the unit type anyway.

85

I understand that we do not really support DWARF64, but why make it completely fatal. We could still extract the length of the unit and skip it, right? Of course, I doubt many programs link DWARF64 with 32...

87–98

Why not emit the verifier errors from inside the verifyUnit() function? This would remove the need for the VerifyFlags struct. I believe it would also make the logic in verifyUnit simpler.

sgravani updated this revision to Diff 106316.Jul 12 2017, 2:45 PM
sgravani marked 7 inline comments as done.

Addressed reviewer's comments.

sgravani added inline comments.Jul 12 2017, 2:46 PM
lib/DebugInfo/DWARF/DWARFVerifier.cpp
85

I'm not exactly sure how to "skip" the unit.. I would need to be handling 64-bit offsets in order to get the next Unit in the .debug_info section. We could do that, but at the moment I prefer not to deal with DWARF64 and stop the verification when we identify a unit in the the 64-bit format.

Just one comment:

lib/DebugInfo/DWARF/DWARFVerifier.cpp
103–104

I still don't like that isUnitDWARF64 is a member when it's used only for communicating between handleDebugInfoUnitHeaderChain() and verifyUnit(). I think I'd prefer is it was a local variable passed as an out-parameter to verifyUnit.

sgravani updated this revision to Diff 106537.Jul 13 2017, 2:27 PM
  • Made isUnitDWARF64 local variable
  • Added description for verifyUnitHeader()
sgravani marked an inline comment as done.Jul 13 2017, 2:28 PM
friss accepted this revision.Jul 13 2017, 2:59 PM

LGTM thanks!

This revision is now accepted and ready to land.Jul 13 2017, 2:59 PM
sgravani added inline comments.Jul 13 2017, 4:26 PM
lib/DebugInfo/DWARF/DWARFVerifier.cpp
103–104

Changed initialization of isUnitDWARF64 to false so that the while loop that goes over units doesn't break after a failure in verifyUnitHeader().