This is an archive of the discontinued LLVM Phabricator instance.

[ELF] Refactor ObjFile<ELFT>::initializeSymbols to enforce the invariant: InputFile::symbols has non null entry
ClosedPublic

Authored by MaskRay on Jun 16 2020, 8:22 PM.

Details

Summary

Fixes PR46348.

ObjFile<ELFT>::initializeSymbols contains two symbol iteration loops:

for each symbol
  if non-inheriting && non-local
    fill in this->symbols[i]

for each symbol
  if local
    fill in this->symbols[i]
  else
    symbol resolution

Symbol resolution can trigger a duplicate symbol error which will call
InputSectionBase::getObjMsg to iterate over InputFile::symbols. If a
non-local symbol appears after the non-local symbol being resolved
(violating ELF spec), its this->symbols[i] entry has not been filled
in, InputSectionBase::getObjMsg will crash due to
dyn_cast<Defined>(nullptr).

To fix the bug, reorganize the two loops to ensure this->symbols is
complete before symbol resolution. This enforces the invariant:
InputFile::symbols has none null entry when InputFile::getSymbols() is called.

for each symbol
  if non-inheriting
    fill in this->symbols[i]

for each symbol starting from firstGlobal
  if non-local
    symbol resolution

Additionally, move the (non-local symbol in local part of .symtab)
diagnostic from Writer<ELFT>::copyLocalSymbols() to initializeSymbols().

Diff Detail

Event Timeline

MaskRay created this revision.Jun 16 2020, 8:22 PM

Thanks for the fix! It looks reasonable to me, but before approving, I wanted to ask would we be better off emitting some kind of warning or error about the out-of-order list earlier on?

lld/ELF/InputSection.cpp
350 ↗(On Diff #271268)

It might be worth a comment saying under what conditions b can be nullptr.

would we be better off emitting some kind of warning or error about the out-of-order list earlier on?

+1 to this question.

lld/test/ELF/invalid/broken-symtab-duplicate-symbol.test
2 ↗(On Diff #271268)

I do not think you should mention the particular internal API names.
This comment should be more general probably. Like:

In this test we have a local symbol that follows a global one. This is not correct .... Check that ...
15 ↗(On Diff #271268)

OSABI is not needed.

28 ↗(On Diff #271268)

Seems there is a double spacing here and above (Sections).

MaskRay marked 5 inline comments as done.Jun 17 2020, 9:08 AM

would we be better off emitting some kind of warning or error about the out-of-order list earlier on?

+1 to this question.

This should be a separate change. llvm-readobj -s should emit a warning as well (like GNU readelf).

lld/test/ELF/invalid/broken-symtab-duplicate-symbol.test
2 ↗(On Diff #271268)

This test is coupled with the implementation. If the implementation changes, we'll have to adjust the test. Exposing a bit of implementation may be fine to remind a reader to adjust the test. I'll amend the comment a bit.

MaskRay updated this revision to Diff 271387.Jun 17 2020, 9:14 AM
MaskRay marked an inline comment as done.

Add comment to code. Improve test.

jhenderson accepted this revision.Jun 17 2020, 11:57 PM

I think this is okay. LGTM, with nit.

lld/test/ELF/invalid/broken-symtab-duplicate-symbol.test
2 ↗(On Diff #271387)

Nit: spurious '#' at end of line.

This revision is now accepted and ready to land.Jun 17 2020, 11:57 PM

would we be better off emitting some kind of warning or error about the out-of-order list earlier on?

+1 to this question.

This should be a separate change. llvm-readobj -s should emit a warning as well (like GNU readelf).

Shouldn't we just error out early?

We have a few more places where we have this code pattern:

  1. MapFile.cpp

https://github.com/llvm/llvm-project/blob/master/lld/ELF/MapFile.cpp#L54

  1. InputSectionBase::getEnclosingFunction:

https://github.com/llvm/llvm-project/blob/master/lld/ELF/InputSection.cpp#L285

That code also uses dyn_cast<Defined>(b) and makes me wondering why do you want to support
this broken case instead of reporting an error much earlier? This ways seems we will not need to
fix all other places.

would we be better off emitting some kind of warning or error about the out-of-order list earlier on?

+1 to this question.

This should be a separate change. llvm-readobj -s should emit a warning as well (like GNU readelf).

Shouldn't we just error out early?

Yes.

We have a few more places where we have this code pattern:

  1. MapFile.cpp

https://github.com/llvm/llvm-project/blob/master/lld/ELF/MapFile.cpp#L54

  1. InputSectionBase::getEnclosingFunction:

https://github.com/llvm/llvm-project/blob/master/lld/ELF/InputSection.cpp#L285

That code also uses dyn_cast<Defined>(b) and makes me wondering why do you want to support
this broken case instead of reporting an error much earlier? This ways seems we will not need to
fix all other places.

The two functions are called in the relocation pass, which is very late and is not subject to the bug.
I do think we have a more serious issue which my original description failed to capture.
Updating...

MaskRay updated this revision to Diff 271806.Jun 18 2020, 12:04 PM
MaskRay retitled this revision from [ELF] Fix a dyn_cast<Defined>(nullptr) crash if a local symbol appears in InputFile::symbols to [ELF] Refactor ObjFile<ELFT>::initializeSymbols to keep the invariant: InputFile::symbols has non null entry.
MaskRay edited the summary of this revision. (Show Details)

Refactor ObjFile<ELFT>::initializeSymbols instead

MaskRay updated this revision to Diff 271815.Jun 18 2020, 12:41 PM
MaskRay retitled this revision from [ELF] Refactor ObjFile<ELFT>::initializeSymbols to keep the invariant: InputFile::symbols has non null entry to [ELF] Refactor ObjFile<ELFT>::initializeSymbols to enforce the invariant: InputFile::symbols has non null entry.
MaskRay edited the summary of this revision. (Show Details)

Move a diagnostic from Writer to initializeSymbols

MaskRay edited the summary of this revision. (Show Details)Jun 18 2020, 12:43 PM
MaskRay edited the summary of this revision. (Show Details)
grimar accepted this revision.Jun 19 2020, 12:58 AM

LGTM with a nit.

lld/ELF/InputFiles.cpp
1062

You can do:

if (eSym.getBinding() != STB_LOCAL) {

and inline STB_LOCAL value.

jhenderson added inline comments.Jun 19 2020, 12:59 AM
lld/ELF/InputFiles.cpp
1100

Isn't this going to result in reading every global symbol twice? That seems like a negative performance hit to me, and I'm not sure it's necessary either (the two loops could still be a single loop it seems to me).

lld/test/ELF/invalid/symtab-sh-info-dup.test
6

an null -> a null
to null pointer -> to a null pointer

MaskRay marked 4 inline comments as done.Jun 19 2020, 8:54 AM
MaskRay added inline comments.
lld/ELF/InputFiles.cpp
1100

There were two loops before and two loops now. After refactoring there is strictly less work: the second loops starts from firstGlobal (was 0).

(the two loops could still be a single loop it seems to me)

It will be very difficult.

MaskRay updated this revision to Diff 272097.Jun 19 2020, 8:55 AM
MaskRay marked an inline comment as done.

Address comments

This revision was automatically updated to reflect the committed changes.
jhenderson added inline comments.Jun 22 2020, 12:25 AM
lld/ELF/InputFiles.cpp
1100

Ah, sorry, the way the diff lined up made it look like only one loop on the left. Thanks.