This is an archive of the discontinued LLVM Phabricator instance.

[ELF2] Implements -z relro: create an ELF "PT_GNU_RELRO" segment header in the object.
ClosedPublic

Authored by grimar on Nov 1 2015, 9:21 AM.

Details

Summary

Partial (-z relro) and full (-z relro, -z now) relro cases are implemented.

Partial relro:

  • The ELF sections are reordered so that the ELF internal data sections (.got, .dtors, etc.) precede the program's data sections (.data and .bss).

.got is readonly, .got.plt is still writeable.

Full relro:

  • Supports all the features of partial RELRO, .got.plt is also readonly.

Diff Detail

Event Timeline

grimar updated this revision to Diff 38852.Nov 1 2015, 9:21 AM
grimar retitled this revision from to [ELF2] Implements -z relro: create an ELF "PT_GNU_RELRO" segment header in the object..
grimar updated this object.
grimar added reviewers: ruiu, rafael.
grimar added subscribers: llvm-commits, grimar.
davide added a subscriber: davide.Nov 1 2015, 9:37 AM
davide added inline comments.
ELF/Driver.cpp
192

You already assign ZRelro to false so the last two lines (I think) can be removed, no?

grimar added inline comments.Nov 1 2015, 9:57 AM
ELF/Driver.cpp
192

I am not sure about that. What will be if we specify both ones in the command line (-z relro -z norelro ... -z relro) ? I guess that processing both ones can also be helpfull here since that can be feature that is already used and works for overriding the option for somebody.

But if first argument is not fine then it is still questionable which exactly lines should be removed then.
Like notes mentioned: looks like ld has ZRelro set to true by default.
So I need to know should I do the same in this patch or not. If yes then not the last ones, but two lines before the last two are becoming redundant.

davide added inline comments.Nov 1 2015, 10:26 AM
ELF/Driver.cpp
192

I think -zrelro -znorelro -zlrelro is a fairly weird way of specifying options on a cmdline (probably we shouldn't really care about that at this stage of development) but just out of curiosity, can you please check what gold and ld do in this case? My experience is that the option to enable "wins" but they're not always consistent with themselves.

grimar added inline comments.Nov 2 2015, 12:47 AM
ELF/Driver.cpp
192

I would agree that we should not care, but not for current case when it is just 2 lines of code that are already written and keeps consistenty with both ld/gold:

gcc -fuse-ld=bfd -Wl,-z,norelro,-z,relro,-z,norelro -fPIC maintest.c -o mainro
gcc -fuse-ld=gold -Wl,-z,norelro,-z,relro,-z,norelro -fPIC maintest.c -o mainro
Both ld/gold does not generate GNU_RELRO headers in that cases above.

Adding one more -z relro makes header to appear in output:
gcc -fuse-ld=bfd -Wl,-z,norelro,-z,relro,-z,norelro,-z,relro -fPIC maintest.c -o mainro

rafael added inline comments.Nov 6 2015, 1:23 PM
ELF/Writer.cpp
329

Please don't do this.

The constraint that we have is that now the RW PT_LOAD is split in two parts. The bits that become ro after relacations are applied and the ones that don't.

You can implement that directly in the comparison function.

Also, I think we may as well always sort like that.

rafael edited edge metadata.Nov 6 2015, 1:26 PM
rafael added a subscriber: rafael.

And please rebase on top of trunk :-)

grimar updated this revision to Diff 39684.Nov 9 2015, 5:46 AM
grimar edited edge metadata.

Rebased, simplified.

ELF/Writer.cpp
329

Problem is I cant recognize where is "got", "got.plt" and "data" using only section attributes bits.
They all are SHT_PROGBITS and "aw". And the only way to put .data to be the last here is checking its name I think.

There was no need to check order of got and got.plt because of stable_sort here, so I removed getRelroSectionRank(), but anyways here and in assignAddresses() I was not able to get rid of compare of name with ".data".

rafael added inline comments.Nov 9 2015, 6:57 AM
ELF/Writer.cpp
386

This needs a comment and should probably be more explicit. Something like

bool AIsRelRo = helperfunc(...)
bool BIsRelRo = helperfunc(...)
if (AIsRelRo != BIsRelRo)

return AIsRelRo;
768

You should be able to write this as

if (cur is rw and sec is relro)

Add sec to relro segment.

The "is relro" check should use the same helper function that is used in the sorting.

ruiu edited edge metadata.Nov 9 2015, 11:53 AM

This is probably naive but obligatory question. Is relro segments considered actually useful? How many projects are using this for what?

ELF/Driver.cpp
192

Why do they have norelro although they don't have nonow nor noorigin or such?

ELF/Writer.cpp
763

I don't really want to make this for-loop longer than that is now. This is getting hard to understand over time. Can you create another for loop and move this piece of code outside of the loop?

grimar added inline comments.Nov 10 2015, 12:33 AM
ELF/Driver.cpp
192

I guess because -relro is set by default in bfd. Then the question is why they have -relro. Thats I dont know.

In D14218#285384, @ruiu wrote:

This is probably naive but obligatory question. Is relro segments considered actually useful? How many projects are using this for what?

For me it looks like its extremely useful for security. List of projects can be found here for example:
http://tk-blog.blogspot.ru/2009/02/relro-not-so-well-known-memory.html

grimar updated this revision to Diff 39821.Nov 10 2015, 10:07 AM
grimar edited edge metadata.

Review comments addressed.

ELF/Writer.cpp
386

Done.

763

Adding another loop would add too much of code and too much duplication. Bcos I still need to calculate VA. I moved that logic to external method instead.

768

Done.

rafael added inline comments.Nov 10 2015, 2:03 PM
ELF/Writer.cpp
327

This has to be a whitelist.

If a .o file has a section name "foobar", we cannot assume that it is relro.

692

delete the empty line.

ruiu added inline comments.Nov 11 2015, 2:24 PM
ELF/Driver.cpp
192

We probably should enable this by default, too.

ELF/Writer.cpp
48

You named isRelroSection and GnuRelroPhdr, so you want to name this updateRelro (instead of updateRelRo) for consistency.

693
if (!Config->ZRelro || !(Cur->p_flags & PF_W) || isRelroSection(Sec))
  return;
700

Remove this blank line.

803

So you are checking if p_filesz is not zero here to determine if we need the phdr, but

839–840

Here you are using something different. Are they guaranteed to be the same?

grimar updated this revision to Diff 40266.Nov 16 2015, 2:57 AM

Review comments addressed.

ELF/Writer.cpp
327

Done.

692

Done.

693

Done.

700

Done.

839–840

Fixed

rafael added inline comments.Nov 18 2015, 5:15 PM
ELF/Writer.cpp
333

Thin can be just Sec == &GotPlt, no?

336

Check for &Dynamic and &Got instead of the names.

grimar added inline comments.Nov 18 2015, 11:26 PM
ELF/Writer.cpp
336

I tried that and the above when developed it to see and that made method to look a bit more bulky. It adds one more _if_ section and for me comparing of names looks better since we anyways unable to get rid of that at all and dont plan to rename the sections ever.
So do you think its really be better to do ?

grimar updated this revision to Diff 40761.Nov 20 2015, 3:31 AM
  • Addressed review comments
  • Rebased
rafael accepted this revision.Nov 23 2015, 12:02 PM
rafael edited edge metadata.

LGTM with two small changes.

ELF/Writer.cpp
386

Add a comment saying that relro go before plain rw sections.

622

Use |=

This revision is now accepted and ready to land.Nov 23 2015, 12:02 PM

LGTM with two small changes.

Perfect. thanks for your time !

This revision was automatically updated to reflect the committed changes.
grimar updated this object.Nov 24 2015, 1:47 AM
grimar edited edge metadata.
emaste added a subscriber: emaste.EditedDec 9 2015, 11:46 AM

With this change we end up with broken binaries on FreeBSD: http://llvm.org/pr25790

With this change we end up with broken binaries on FreeBSD: http://llvm.org/pr25790

http://reviews.llvm.org/D15423 should fix that.