Sources Bugzilla – Bug 7065
Support building glibc with -fstack-protector or -fstack-protector-all
Last modified: 2012-04-21 12:55:37 UTC
Several distributions, including Gentoo, have patches which attempt to make glibc build when compiled with -fstack-protector. None of them that I have found actually work: at best, they make it crash instantly, because none of them suppress generation of stack guards around functions called before the guard is initialized (or even before ld-linux.so.2 has relocated itself). For now I'd consider it more of a maintenance burden than it's worth to isolate the parts of ld.so that are called before security_init(), so I've just arranged to avoid using the stack-protector in ld.so, and marked the few things not also in the dynamic loader which are used during pre-guard-init static library initialization with -fno-stack-protector. (An improvement might be to mark these with -fno-stack-protector only when building the static library.) I can't think of a way to isolate the functions in this set automatically, but if you get it wrong it's easy to tell because you get an instant coredump, so it's easy to prevent the set of -fno-stack-protectored functions from bitrotting, manually maintained though it must be.) You can still use -fstack-protector-all in all the rest of glibc, which is an order of magnitude more code than ld.so and includes really hairy stuff like malloc() and lots of functions that themselves call string-manipulation functions, so I'd say this is a pretty good tradeoff. Some of the module tests need adjustment: they don't link against libc, so must specify -fno-stack-protector; the same is true of some of the configury, taking care not to specify that unless GCC is actually capable of accepting -fstack-protector. (We specify -lssp and leave the stack protector on where possible in configure tests.) Results of tests and patch against glibc 2.9 following shortly.
Never going to happen.
test environment: 2.6.27.7 kernel+headers, glibc 2.8, GCC 4.3.3 20081121 (prerelease), binutils 2.19. config flags (my standard set for this machine): /usr/packages/glibc/2.9/configure --prefix=/usr --enable-shared \ --enable-profile --disable-bounded --enable-bind-now \ --enable-add-ons=nptl,libidn --enable-kernel=2.6.25 \ --enable-check-abi=warn --enable-omitfp \ --enable-stackguard-randomization TIMEOUTFACTOR=5 Below, PASS means 'baseline test failures only'. (I also compared configure output to verify that -fstack-protector addition did not change the results of any configure tests, and verified that the appropriate -fstack-protector actually appeared in gcc commandlines at the appropriate times.) baseline: PASS, by definition patch applied, no flags specified: PASS -fstack-protector in CFLAGS: PASS -fstack-protector-all in CFLAGS: one failure due to #7066, buffer overrun --without-stack-protector: PASS --with-stack-protector: PASS --with-stack-protector=all: one failure due to #7066, buffer overrun Test failures for baseline (unpatched): math/test-ildoubl.out: testing long double (inline functions) Failure: Test: expm1 (1) == M_El - 1.0 Result: is: 1.71828182845904523532e+00 0xd.bf0a8b14576953500000p-3 should be: 1.71828182845904523543e+00 0xd.bf0a8b14576953600000p-3 difference: 1.08420217248550443401e-19 0x8.00000000000000000000p-66 ulp : 1.0000 max.ulp : 0.0000 Maximal error of `expm1' is : 1 ulp accepted: 0 ulp Test suite completed: 3618 test cases plus 3005 tests for exception flags executed. 2 errors occurred. elf/check-localplt.out: --- ../scripts/data/localplt-i386-linux-gnu.data 2006-01-11 21:06:19.000000000 +0000 +++ - 2008-11-30 20:52:09.962033876 +0000 @@ -1,4 +1,5 @@ libc.so: _Unwind_Find_FDE +libc.so: __bzero libc.so: calloc libc.so: free libc.so: malloc (This looks like something missing from localplt-i386-linux-gnu.data to me, not a bug.) (Holes in test coverage: not tested with a GCC too old to support -fstack-protector. Static testing not performed: see #7064.)
Created attachment 3087 [details] stack protector support for glibc This is posted at the request of Carlos O'Donell. TBH I don't care if it doesn't go upstream, although given that it's already found a buffer overrun in glibc I'd find that surprising. I'm more interested in distros picking it up. (And 'never going to happen' is a peculiar statement. It has 'happened'. The patch *exists*.)
Nix A failure in elf/check-localplt.out as indicated by a new symbol in scripts/data/localplt-i386-linux-gnu.data means that libc proper is invoking the new symbol via the plt when in-fact it should be making a direct invocation to a libc internal symbol. There are very few conditions under which this is allowed (notably those cases where we allow libc functionality to be overridden). I've looked through the code and it appears that the sunrpc code is the only relevant code which uses __bzero. I don't think there's an internal hidden version of the symbol. So these calls to __bzero probably shouldn't be there. Instead, they should use memset. Or perhaps that patch which you're using uses __bzero? In order to verify, one can look at the symbol table: objdump -DR libc.so > libc.dis Search libc.dis for: __bzero@plt You should see a plt call stub, e.g. 00016198 <__bzero@plt>: 16198: ff a3 0c 00 00 00 jmp *0xc(%ebx) 1619e: 68 00 00 00 00 push $0x0 161a3: e9 e0 ff ff ff jmp 16188 <h_errno+0x16168> Now search for: "call 16198" call 16198 <__bzero@plt> This should bring you to the disassembly of the function which invoked __bzero via the PLT. You can then go into the C source file and replace this with a memset. Do this for all calls to the address for __bzero.
Here's a more thorough write-up of the same thing I just posted: http://sources.redhat.com/glibc/wiki/Testing/Check-localplt Invocation of __bzero() by the sunrpc code is acceptable since that code is in a different library than libc.so so access via the PLT is expected.
Nice description, Ryan :) A lot of the sunrpc code *does* land in libc (all the client code). Notably, bindresvport(), clnt_create(), clnt_broadcast(), universal() (called from registerrpc() via a callback from svc_register()), svctcp_create(), svcudp_bufcreate(), and key_gendes(), _des_crypt() (obviously used for DES-encrypted SunRPC), all explicitly call __bzero() and land in libc. Almost certainly these would use memset() were the SunRPC code not ancient Sun-derived gunge with a 1986 copyright date...
Keep it going, guys. To Ulrich Drepper: How do you mean: "Never going to happen."?! I always keen on people ignoring security measures. Regards, Dw.
I've been using a compromise. Glibc's programs can be compiled with -fstack-protector-all, or whatever other options you may want (-D_FORTIFY_SOURCE=2, -fPIE, etc), but not the libraries. I use the configparms file and set build-programs=no to build the libraries without -fstack-protector, then remove build-programs=no and add 'CFLAGS += -fstack-protector-all' to configparms. No patches needed, test suites pass (remove -fstack-protector during the test suite), no crashes.
Created attachment 6248 [details] stack protector support against eglibc 2.13. This is the most recent version of this patch, against eglibc 2.13 (because that happens to be the version I'm using now, as I track Debian's glibc). It has needed no significant revisions for years, though the recent csu changes in upstream glibc may necessitate some small revisions. (The ChangeLog is out of date: I haven't regenerated it since 2008.)
Your change contains two different changes: * Supporting stack-protector * A different implementation of chk_fail function For addition to glibc, I would only look at the stack-protector support. I suggest you continue discussing this on the libc-alpha list.