5 ms·
Not exactly the best example. If I take this code (test.c) and try to compile it: > gcc test.c I get an error: test.c:21:3: warning: function returns add
by reza_n 9y ago
Not exactly the best example. If I take this code (test.c) and try to compile it:
> gcc test.c
I get an error:
test.c:21:3: warning: function returns address of local
variable [-Wreturn-local-addr]
return &vec;
After I fix that error, the program segfaults when running. Compile with asan:
> gcc test.c -Fsanitize=address -lasan -g
Then we can start debugging these problems:
==3261== ERROR: AddressSanitizer: attempting double-free on 0x60040000dfd0:
...
#2 0x4008b5 in vec_free ./test.c:46
Not trying to say that this is the best workflow for debugging C, but the tooling does exist for these kinds of programming errors.
- reza_n 9y agoAs another poster pointed out, if I run this via valgrind (after fixing the first error): > valgrind ./a.out ==4871== Invalid write of size 4 ==4871== at 0x40073A: vec_push (test.c:40) ==4871== Invalid write of size 4 ==4871== at 0x40073A: vec_push (test.c:40) ==4871== Invalid read of size 4 ==4871== at 0x4007CD: main (test.c:55) ==4871== Invalid read of size 8 ==4871== at 0x400769: vec_free (test.c:46) ==4871== Invalid free() / delete / delete[] / realloc() ==4871== at 0x4C2BDEC: free (...) ==4871== by 0x400770: vec_free (test.c:46)
- amluto 9y agoNone of which will help detect the integer overflow unless you're quite serious about your testing.
- reza_n 9y agoVery valid point. The code for that: int new_capacity = vec->capacity * 2; assert(new_capacity > vec->capacity);
- nbsd4lyfe 9y agoasserts are for diagnostic-enabled code, don't use them for security checks.
- steveklabnik 9y agoThis is one area where Rust also differs from C; assert!s are left on in release mode; you use debug_assert! if you want something only in development.
- nbsd4lyfe 9y agoI agree, it's a hella questionable language choice, and not limited to C. I found out about it through https://github.com/rohe/pysaml2/issues/451 https://github.com/rohe/pysaml2/issues/451
- reza_n 9y agoI would argue the opposite, assert() statements are the best way to write defensive and secure C. There might have been a time when people commonly compiled out assert() statements from binaries, but that is only OK if the software was designed for that. Otherwise, that would be like me saying I am going to compile out all strlen() statements from a given binary and then expect it to behave the same.
- irundebian 9y agoSecure code should be correct and robust. To assure correctness assert()'s should be used, to assure robustness you should check return values and buffer sizes.
- amluto 9y agoNot good enough. The compiler is entirely within its rights to optimize that out, since signed integer overflow is UB in C.
- reza_n 9y agoOverflow is a runtime behavior, not a compile time behavior. How a type overflows is dependent on the CPU, not software.
- koopa184 9y agoThe C standard explicitly lists signed integer overflow as an undefined behavior. Any undefined behavior in your program essentially makes it semantically meaningless. There is no guarantee that the compiler will even emit the assembly instructions that would lead to signed overflow behavior. It could just as well emit a program that deletes your files, and still be standards-compliant.
- nkurz 9y agoYou are either trolling or dangerously wrong. A modern optimizing compiler will simply remove your assert() from the generated assembly. If you are trolling, stop it. But if you are a C programmer and speaking honestly, you need to have a better understanding of undefined behavior in C before you write any more code. Checking for signed overflow in C is extremely difficult to do correctly. This is a good starting point: https://blog.regehr.org/archives/213 https://blog.regehr.org/archives/213.
- reza_n 9y agoNothing in the snippet is undefined. The assert simply enforces the size always increases. Not sure I understand why you think the assert() gets removed. Is this documented somewhere? Hint: vec->capacity could be 0
- dpc_pw 9y agoAFAIK you are wrong, and the other poster is right. Signed overflow (in int * 2) is an UB. If it happens, compiler is free to do anything. Eg. assume your assert can never happen and remove it. Let me explain: Because signed overflow is an UB, compiler assumes that you would never write a code that overflows like that (because you write C, the assupmtion is that you are omnipotent), and knowing that, remove the assert that could possibly never ever happen, since it's a dead code anyway. I've spend more than 6 years writting embedded software in C. God I love Rust.