Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider applying -Wl,-z,relro or -Wl,-z,relro,-z,now by default #29877

Closed
brson opened this issue Nov 16, 2015 · 18 comments
Closed

Consider applying -Wl,-z,relro or -Wl,-z,relro,-z,now by default #29877

brson opened this issue Nov 16, 2015 · 18 comments
Labels
A-linkage Area: linking into static, shared libraries and binaries C-enhancement Category: An issue proposing an enhancement or a PR with one. P-low Low priority

Comments

@brson
Copy link
Contributor

brson commented Nov 16, 2015

These are some options that Debian applies, and that @gus wants to apply to all Rust code. Generally, Rust likes to harden things by default when there are no obvious disadvantages. Is it feasible that we might just use these by default?

@brson brson added the A-linkage Area: linking into static, shared libraries and binaries label Nov 16, 2015
@gus
Copy link

gus commented Nov 16, 2015

Sounds cool, but I didn't ask for that, @brson :)

Cheers,
Gus

@brson
Copy link
Contributor Author

brson commented Nov 17, 2015

Oops, wrong @gus. Sorry! Strong beard though.

@gus
Copy link

gus commented Nov 17, 2015

;)

On Mon, Nov 16, 2015, 20:55 Brian Anderson notifications@github.com wrote:

Oops, wrong @gus https://github.com/gus. Sorry! Strong beard though.


Reply to this email directly or view it on GitHub
#29877 (comment).

@genodeftest
Copy link
Contributor

There is something similiar on Fedora: Change: Harden all packages

@brson brson added the P-low Low priority label May 4, 2017
@nagisa
Copy link
Member

nagisa commented May 4, 2017

Downside is that not every alternative linker out there might support the flags.

@brson brson added the C-enhancement Category: An issue proposing an enhancement or a PR with one. label May 4, 2017
@brson
Copy link
Contributor Author

brson commented May 4, 2017

@alexcrichton do recall if we've done anything here? I don't see 'relro' in the source.

@alexcrichton
Copy link
Member

No I don't believe we ever implemented this, but would be good to do so!

@alexcrichton
Copy link
Member

er didn't mean to close

@cuviper
Copy link
Member

cuviper commented May 4, 2017

FWIW, I also apply -Clink-arg=-Wl,-z,relro,-z,now in Fedora.

kyrias added a commit to kyrias/rust that referenced this issue Jul 11, 2017
This commit adds support for full RELRO, and enables it for the
platforms I know have support for it.

Full RELRO makes the PLT+GOT data read-only on startup, preventing it
from being overwritten.

http://tk-blog.blogspot.com/2009/02/relro-not-so-well-known-memory.html

Fixes rust-lang#29877.

Signed-off-by: Johannes Löthberg <johannes@kyriasis.com>
bors added a commit that referenced this issue Jul 19, 2017
Add support for full RELRO

This commit adds support for full RELRO, and enables it for the
platforms I know have support for it.

Full RELRO makes the PLT+GOT data read-only on startup, preventing it
from being overwritten.

http://tk-blog.blogspot.com/2009/02/relro-not-so-well-known-memory.html

Fixes #29877.

---

I'm not entirely certain if this is the best way to do it, but I figured mimicking the way it's done for PIE seemed like a good start at least.  I'm not sure whether we want to have it enabled by default globally and then disabling it explicitly for targets that don't support it though.  I'm also not sure whether the `full_relro` function should call `bug!()` or something like it for linkers that don't support it rather than no-opping.
@OfekShilon
Copy link

Why not build with -no-plt instead? Seems a bit funny to erect the entire PLT apparatus to support lazy loading and then populate it at program startup.

@bjorn3
Copy link
Member

bjorn3 commented Sep 28, 2023

Using a GOT instead will cause every function call to require a load of the address, even if the target is guaranteed to resolve in the same DSO. When emitting relocations for a PLT, the linker can replace PLT calls with direct relative calls in case of non-exported symbols AFAIK which is almost all calls in an average Rust program.

@OfekShilon
Copy link

@bjorn3 I think PLT relocations must resolve to GOT slots anyway.

@bjorn3
Copy link
Member

bjorn3 commented Sep 29, 2023

A PLT relocation for an external symbol is implemented as pc-relative call to a stub function which loads from the GOT. For an internal symbol this can trivially be relaxed to a pc-relative call to the actual function. The stub function is generated by the linker, not the compiler, so the linker has full control over it. For a GOT relocation however the load is part of the code emitted by the compiler and can't be replaced.

@OfekShilon
Copy link

@bjorn3 internal symbols with "default" visibility are still interposable, and the compiler/linker are forced* to place a GOT entry for them - to account for the case that another binary preempts them.

*clang sometimes optimizes these calls anyway, but in doing so it knowingly breaks the ABI.

@bjorn3
Copy link
Member

bjorn3 commented Sep 29, 2023

Rust uses -fno-semantic-interposition just like clang defaults to. Even if we didn't there wouldn't be any way to interpose mangled rust symbols without depending on implementation details. Symbol names are not stable and depend on the exact rustc version as well as whatever cargo passes as -Cmetadata argument. Only #[no_mangle] symbols can safely be interposed, and those are never internal anyway. Also how can internal symbols be interposed in the first place? They don't end up in .dynsym and as such the dynamic linker should know nothing about them. Rustc is very careful to avoid leaking any rust symbol from cdylibs and bins.

@OfekShilon
Copy link

OfekShilon commented Sep 29, 2023

@bjorn3 This is an empirical matter. This toy test:

$ cat main.cpp
void exefunc() {}

int main() {
    exefunc();
    return 0;
}
$
$ g++ -o exe_gcc main.cpp
$ readelf --sections exe_clang | grep got$ readelf --sections exe_gcc | grep got
  [13] .plt.got          PROGBITS         0000000000001030  00001030
  [22] .got              PROGBITS         0000000000003fc0  00002fc0
$ 
$ g++ -o exe_gcc main.cpp -fno-plt
$ readelf --sections exe_gcc | grep got
  [13] .plt.got          PROGBITS         0000000000001030  00001030
  [22] .got              PROGBITS         0000000000003fc0  00002fc0
$ 
$ clang++ -o exe_clang main.cpp
$ readelf --sections exe_clang | grep got
  [19] .got              PROGBITS         0000000000403ff0  00002ff0
  [20] .got.plt          PROGBITS         0000000000404000  00003000
$ 
$ clang++ -o exe_clang main.cpp -fno-plt
$ readelf --sections exe_clang | grep got
  [19] .got              PROGBITS         0000000000403ff0  00002ff0
  [20] .got.plt          PROGBITS         0000000000404000  00003000

Seems to show that with or without PLT, in both gcc and clang, the GOT is used. Am I missing something?

@OfekShilon
Copy link

Rust uses -fno-semantic-interposition just like clang defaults to.

Actually this switch just avoids the interposition infrastructure for calls into the same translation unit.

Even if we didn't there wouldn't be any way to interpose mangled rust symbols without depending on implementation details. Symbol names are not stable and depend on the exact rustc version as well as whatever cargo passes as -Cmetadata argument. Only #[no_mangle] symbols can safely be interposed, and those are never internal anyway.

Indeed, the GOT is redundant for rust - and largely for C++ too. Yet this is not the point in question: The point is that PLT is redundant. The PLT isn't there to facilitate interposition, it is there to facilitate lazy binding. And this merge shows that rust (rightfully) avoids lazy binding anyway, so why pay the extra overhead?

Also how can internal symbols be interposed in the first place? They don't end up in .dynsym and as such the dynamic linker should know nothing about them.

You're right, internal symbols have hidden visibility and cannot be interposed. Again, this is a side discussion and not the point of my comment.

@bjorn3
Copy link
Member

bjorn3 commented Sep 30, 2023

We don't yet know when compiling object files if non-local rust symbols will be looked up inside the same DSO or outside. Only once we start linking do we know this. This rules out using non-PLT PC-relative relocations for position independent executables. So PLT or GOT relocations are the only option. GOT relocations can't be relaxed to regular PC-relative calls, so PLT relocations it is.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-linkage Area: linking into static, shared libraries and binaries C-enhancement Category: An issue proposing an enhancement or a PR with one. P-low Low priority
Projects
None yet
Development

No branches or pull requests

8 participants