8 ms·
GCC's new fortification level: The gains and costs (2022)
- loeg 2y ago(2022)
- Someone 2y agoIt also doesn’t deliver what it promises, as it doesn’t discuss the cost well. The only thing it states is that it increases code size, but it doesn’t given give numbers, and the header “The gains of improved security coverage outweigh the cost” doesn’t describe its content, which says: “We need a proper study of performance and code size to understand the magnitude of the impact created by _FORTIFY_SOURCE=3 additional runtime code generation. However the performance and code size overhead may well be worth it due to the magnitude of improvement in security coverage.”
- RustyRussell 2y agoYes, came here to say this too. Of course it varies, but some measurements would be nice!
- jwilk 2y agoDiscussed back then: https://news.ycombinator.com/item?id=32888516 https://news.ycombinator.com/item?id=32888516 (40 comments)
- rdtsc 2y agoJust curious, does anyone run with _FORTIFY_SOURCE=3 in production? Did you catch any overflows because of it, and most importantly, is there a noticeable performance degradation?
- 9659 2y agoThis brings up a question. I think we can all agree that detecting an overrun shows a fault in the system. But does it create an error? Could this could be changed to where the overflow does not cause an abort; rather the next read of that location, without a corresponding legal write causes the abort. A buffer overrun does not mean the answer is wrong, but the use of memory that was overrun will. In that case, production or not, you would want an abort. The answer is wrong! (Perhaps this makes no sense. If so, sorry, the idea just came to me after reading the article.)
- lmm 2y agoOverrunning the buffer means your program's behaviour is undefined. Continuing to function at all, much less doing what you wanted, merely means you got lucky with the compiler this time.
- saghm 2y agoI tend to argue that continuing to function after a memory overrun is _unlucky_ because it's better to have it fail loudly so you know to fix it instead of it potentially not noticing subtly incorrect behavior.
- forbiddenlake 2y agoArch Linux switched to it last year https://rfc.archlinux.page/0017-increase-fortification-level/ https://rfc.archlinux.page/0017-increase-fortification-level...
- rdtsc 2y agoThanks, that’s encouraging. I think I will give it a try in my own projects.
- jupp0r 2y agoI would imagine false positives could be a huge problem. The behavior if a violation is detected is to gracefully terminate the program, so you could end up with more (but less exploitable) crashes than without FORTIFY_SOURCE.
- jupp0r 2y agoJust to clarify: false positives in the sense that memory safety is violated intentionally as described in the article with relation to realloc and similar hacks, which would normally not cause problems.
- ngneer 2y agoI would encourage the community to find a unit of measurement for security. Every defense can be breached, so every defense has an active region, much like transistors. Question is how to quantify.
- o11c 2y agoThe `realloc` problem isn't the only one - it also breaks many formerly-well-defined programs that use `malloc_usable_size`. Instead of fixing this, the developers behind the "dynamic object size" push have been changing the documentation to declare any use of `malloc_usable_size` buggy even though it used to be explicitly documented as safe. I suspect that GCC's optimization passes will break even C-standard-compliant use of `realloc`, similar to how ASAN can break due to dynamic equality checks between pointers of different provenance. Life would be much simpler for many of us if the standards committee bothered to standardize a function that says "give me a buffer of semi-arbitrary size and tell me how big it is; I promise to resize it later", which is very widely intended. An explicit "realloc in place only, else fail" would also make many more useful programs feasible to write.
- dwattttt 2y ago> give me a buffer of semi-arbitrary size and tell me how big it is; I promise to resize it later I'm not sure how you could use this usefully. If you don't care what size you get, why would you allocate in the first place? And if you do have a minimum size you need right now, and a need for it to be bigger later, isn't that what a malloc/realloc dance is for?
- freeone3000 2y agoExample: reading a file line-by-line. You don’t, can’t, know how big a line is. Your best option is to allocate some random chunk of memory, like 4 kilobytes, read 4 kilobytes from the file into memory, and see if you happened across a newline in there. If you did, shuffle everything around a little / realloc / ringbuffer shenanigans; do your favourite. If you didn’t, make the buffer bigger (by 1.5x? 2x? Log2x?), and try again. This dance is super common with variable length protocols over unframed streams - ie, most things over tcp. So this is an exceptionally common pattern in IO operations. Other common times this pattern happens: finding all items in a list that satisfy a predicate; tracking items a user has added to an interface; cycle detection in graphs; …
- dwattttt 2y ago
- dwattttt 2y ago> the program continued using the same pointer, not the realloc call result, since the old pointer did not change > In this context, it is a bug in the application This is a non-intuitive result by the normal lens C pointers are viewed by; these two pointers compared equal, how could using one differ from using the other? Pointer provenance rears its ugly head here though; one of those "pointers that have the same value"'s provenance is different to the other. By the standard, they _are_ allowed to be treated differently, and honestly the standard requires it. https://www.ralfj.de/blog/2020/12/14/provenance.html https://www.ralfj.de/blog/2020/12/14/provenance.html is pretty accessible, and shockingly on point: "just because two pointers point to the same address, does not mean they are equal in the sense that they can be used interchangeably". EDIT: spelling
- mahkoh 2y agoProvenance might be used as justification now but the actual rules are simpler and stricter. After freeing (or reallocing) a pointer, the application must not inspect the pointer value anymore. Even `new_pointer == old_pointer` is not allowed. IIRC, one justification for this was to account for systems with non-flat memory where inspecting the value of the old pointer might cause a processor exception.
- Scaevolus 2y agoIt also makes sense for an allocator that can satisfy new allocations with recently freed allocations.
- RustyRussell 2y ago> Even `new_pointer == old_pointer` is not allowed. This is legal. But dereferencing old_pointer even after this check had passed is undefined.
- anttihaapala 2y agoNo, it is not, and it has never been OK for as long as C has been standardized by ANSI or ISO. C89 says that "The value of a pointer that refers to freed space is indeterminate." and that behavior is undefined "upon use ... of indeterminately-valued objects", hence a compiled program can e.g. behave as if `new_pointer == old_pointer` even though the object was relocated in memory.
- dwheeler 2y agoFor a comprehensive set of recommended compiler options for hardening C and C++ programs, see this OpenSSF guide: https://best.openssf.org/Compiler-Hardening-Guides/Compiler-Options-Hardening-Guide-for-C-and-C++.html https://best.openssf.org/Compiler-Hardening-Guides/Compiler-...
- throwaway81523 2y agoTitle should be: GCC's new (2022) fortification level... ;)
- jiveturkey 2y agoarticle date 2022 the new feature appeared in 2021
- matheusmoreira 2y agoI wish these compiler protocols and interfaces were better documented. They just assume you're using glibc for support. I want to fully integrate them with my freestanding nolibc C projects but I can't figure out how to do it. The compiler's stack canaries were simple enough. The only issue was the ugly symbols. I requested that a feature be added to override the symbol generated by the compiler so I could use good names instead. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113694 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113694 Things like instrumentation and sanitizers though? No idea. Even asked about it on Stack Overflow and got no answers to this day. https://stackoverflow.com/q/77688456 https://stackoverflow.com/q/77688456 I assume these object sizing builtins make use of function attributes such as malloc, alloc_align and alloc_size. I've added all of those attributes to my memory allocator but I'm not quite sure if they're doing anything useful.
- uecker 2y agoThey will, but glibc also has additional macro wrappers around certain library functions that do explicit checking based on __builtin_object_size or __builtin_dynamic_object_size. The code is public. You could also ask on the gcc mailing list.
- clarionbell 2y agoOddly the example doesn't work on my GCC 13.3.1, regression or just bad copy paste?
- codecitizen94 2y agoSame here for clang 15 :(
- codecitizen94 2y agoOkay ... clang doesn't have it implemented sry
- temac 2y agoUnfortunately _FORTIFY_SOURCE=2 is documented as making some conforming program fail, and it seems hard to find precise informations on this subject, so i'd be hesitant to use it, as well as _FORTIFY_SOURCE=3
- leni536 2y agoFWIW Debian uses _FORTIFY_SOURCE=2 for building its packages for a long time now, so there should be plenty of experience there for using the option.
- notorandit 2y ago> C programs routinely suffer from memory management problems. Again? There is a typo there. - programs + programmers