I am trying to understand why the following code generates an "argument might be clobbered.." warning. Here's a minimal sample:
#include <unistd.h>
extern char ** environ;
int test_vfork(char **args, char **newEnviron)
{
pid_t pid = vfork();
if (pid == 0) {
execve(args[0], args, (newEnviron != NULL)? newEnviron : environ);
}
return 0;
}
And this is the output (this is gcc 4.3.3):
$ gcc -O2 -Wclobbered -c test.c -o test.o
test.c: In function ‘test_vfork’:
test.c:5: warning: argument ‘newEnviron’ might be clobbered by ‘longjmp’ or ‘vfork’
Also I found out that the warning goes away if I replace the execve line with the following:
if (newEnviron != NULL)
execve(commandLine[0], commandLine, newEnviron);
else
execve(commandLine[0], commandLine, environ);
Not sure why gcc likes this better than the original. Can anyone shed some light?
From C99's longjmp description:
All accessible objects have values, and all other components of the abstract
machine218) have state, as of the time the longjmp function was called, except
that the values of objects of automatic storage duration that are local to the
function containing the invocation of the corresponding setjmp macro that do not
have volatile-qualified type and have been changed between the setjmp invocation
and longjmp call are indeterminate.
If you make newEnviron a volatile object, this paragraph indicates that newEnviron will not be clobbered by longjmp. The specification or implementation of vfork might have a similar caveat.
--EDIT--
Because you have optimizations enabled and because newEnviron is non-volatile and because it is not accessed after its use in the ternary conditional operator, one optimization the implementation could perform for your conditional operator would be to actually re-assign newEnviron with the value of environ. Something like:
execve(args[0], args, (newEnviron = newEnviron ? newEnviron : environ));
But we know from the manual for vfork that modifying most objects before the execve results in undefined behaviour.
args doesn't suffer from the same concern because there is no such conditional test.
Your if statement has more sequence points and besides that, might not be as easily recognized as an optimization opportunity. However, I'd hazard a guess that the sequence points play a strong role in the optimization.
The optimization, by the way, is that the storage for newEnviron is repurposed for the result of the conditional operator, meaning neither another register is used (if registers would normally be used) nor additional stack-space is required (for systems using stacks).
I'll bet that if you were able to convince gcc that you needed to access the value of newEnviron after the execve line, the optimization would not be possible and your warning would disappear.
But of course, using volatile is a simple solution, too.
Related
I'm writing my own test-runner for my current project. One feature (that's probably quite common with test-runners) is that every testcase is executed in a child process, so the test-runner can properly detect and report a crashing testcase.
I want to also test the test-runner itself, therefore one testcase has to force a crash. I know "crashing" is not covered by the C standard and just might happen as a result of undefined behavior. So this question is more about the behavior of real-world implementations.
My first attempt was to just dereference a null-pointer:
int c = *((int *)0);
This worked in a debug build on GNU/Linux and Windows, but failed to crash in a release build because the unused variable c was optimized out, so I added
printf("%d", c); // to prevent optimizing away the crash
and thought I was settled. However, trying my code with clang instead of gcc revealed a surprise during compilation:
[CC] obj/x86_64-pc-linux-gnu/release/src/test/test/test_s.o
src/test/test/test.c:34:13: warning: indirection of non-volatile null pointer
will be deleted, not trap [-Wnull-dereference]
int c = *((int *)0);
^~~~~~~~~~~
src/test/test/test.c:34:13: note: consider using __builtin_trap() or qualifying
pointer with 'volatile'
1 warning generated.
And indeed, the clang-compiled testcase didn't crash.
So, I followed the advice of the warning and now my testcase looks like this:
PT_TESTMETHOD(test_expected_crash)
{
PT_Test_expectCrash();
// crash intentionally
int *volatile nptr = 0;
int c = *nptr;
printf("%d", c); // to prevent optimizing away the crash
}
This solved my immediate problem, the testcase "works" (aka crashes) with both gcc and clang.
I guess because dereferencing the null pointer is undefined behavior, clang is free to compile my first code into something that doesn't crash. The volatile qualifier removes the ability to be sure at compile time that this really will dereference null.
Now my questions are:
Does this final code guarantee the null dereference actually happens at runtime?
Is dereferencing null indeed a fairly portable way for crashing on most platforms?
I wouldn't rely on that method as being robust if I were you.
Can't you use abort(), which is part of the C standard and is guaranteed to cause an abnormal program termination event?
The answer refering to abort() was great, I really didn't think of that and it's indeed a perfectly portable way of forcing an abnormal program termination.
Trying it with my code, I came across msvcrt (Microsoft's C runtime) implements abort() in a special chatty way, it outputs the following to stderr:
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
That's not so nice, at least it unnecessarily clutters the output of a complete test run. So I had a look at __builtin_trap() that's also referenced in clang's warning. It turns out this gives me exactly what I was looking for:
LLVM code generator translates __builtin_trap() to a trap instruction if it is supported by the target ISA. Otherwise, the builtin is translated into a call to abort.
It's also available in gcc starting with version 4.2.4:
This function causes the program to exit abnormally. GCC implements this function by using a target-dependent mechanism (such as intentionally executing an illegal instruction) or by calling abort.
As this does something similar to a real crash, I prefer it over a simple abort(). For the fallback, it's still an option trying to do your own illegal operation like the null pointer dereference, but just add a call to abort() in case the program somehow makes it there without crashing.
So, all in all, the solution looks like this, testing for a minimum GCC version and using the much more handy __has_builtin() macro provided by clang:
#undef HAVE_BUILTIN_TRAP
#ifdef __GNUC__
# define GCC_VERSION (__GNUC__ * 10000 \
+ __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
# if GCC_VERSION > 40203
# define HAVE_BUILTIN_TRAP
# endif
#else
# ifdef __has_builtin
# if __has_builtin(__builtin_trap)
# define HAVE_BUILTIN_TRAP
# endif
# endif
#endif
#ifdef HAVE_BUILTIN_TRAP
# define crashMe() __builtin_trap()
#else
# include <stdio.h>
# define crashMe() do { \
int *volatile iptr = 0; \
int i = *iptr; \
printf("%d", i); \
abort(); } while (0)
#endif
// [...]
PT_TESTMETHOD(test_expected_crash)
{
PT_Test_expectCrash();
// crash intentionally
crashMe();
}
you can write memory instead of reading it.
*((int *)0) = 0;
No, dereferencing a NULL pointer is not a portable way of crashing a program. It is undefined behavior, which means just that, you have no guarantees what will happen.
As it happen, for the most part under any of the three main OS's used today on desktop computers, that being MacOS, Linux and Windows NT (*) dereferencing a NULL pointer will immediately crash your program.
That said: "The worst possible result of undefined behavior is for it to do what you were expecting."
I purposely put a star beside Windows NT, because under Windows 95/98/ME, I can craft a program that has the following source:
int main()
{
int *pointer = NULL;
int i = *pointer;
return 0;
}
that will run without crashing. Compile it as a TINY mode .COM files under 16 bit DOS, and you'll be just fine.
Ditto running the same source with just about any C compiler under CP/M.
Ditto running that on some embedded systems. I've not tested it on an Arduino, but I would not want to bet either way on the outcome. I do know for certain that were a C compiler available for the 8051 systems I cut my teeth on, that program would run fine on those.
The program below should work. It might cause some collateral damage, though.
#include <string.h>
void crashme( char *str)
{
char *omg;
for(omg=strtok(str, "" ); omg ; omg=strtok(NULL, "") ) {
strcat(omg , "wtf");
}
*omg =0; // always NUL-terminate a NULL string !!!
}
int main(void)
{
char buff[20];
// crashme( "WTF" ); // works!
// crashme( NULL ); // works, too
crashme( buff ); // Maybe a bit too slow ...
return 0;
}
I don't always use all return values, and sometimes I must handle return values. For instance:
$ make
gcc -pedantic -std=c99 -Wall -O3 -ledit -g -DVERSION=\"v0.160425-2-gc443\" -c -o main.o main.c
main.c: In function ‘int_handler’:
main.c:532:5: warning: ignoring return value of ‘write’, declared with attribute warn_unused_result [-Wunused-result]
write(fileno(stdin), s, sizeof s - 1);
^
gcc -o shell main.o errors.c util.c pipeline.c -ledit
What should I do to avoid the warning? Is there a good way to "use" a variable like writing it to /dev/null so that the compiler won't complain? My own idea is this, which deals with the problem without ignoring the error and I can commit my code without warnings and deal with this later:
void int_handler(int signum) {
if (write(fileno(stdin), s, sizeof s - 1)) {
} else {}
}
Typically to suppress this warning, you can do:
(void)write(fileno(stdin), s, sizeof s - 1);
But the reason for the warning is because write() is declared with an attribute in the header (<unistd.h>) which leads to the warning.
This (attribute) is not done for every function but selectively. For example, printf()'s return is typically ignored.
Since, you would want to check the results of IO operation, it's done for write().
So, if you really want to ignore the result, you can use the (void)func(...); to suppress this.
First of all, in case of file handling functions in production-quality code, you should almost certainly handle all return values.
Otherwise, the standard way to ignore a variable or a function result, is to cast it to (void).
It is a bad idea to not take in account the return value of a call to write, as an I/O may fail it is important to take care of such an event. If you really want to remove the warning, you should explicitly ignore the return value:
(void)write(...);
The prototype for write in the header file unistd.h has been declared as follows
extern ssize_t write (int __fd, const void *__buf, size_t __n) __wur
and __wur has been defined as (in cdefs.h)
# if __USE_FORTIFY_LEVEL > 0
# define __wur __attribute_warn_unused_result__
# endif
#else
# define __attribute_warn_unused_result__ /* empty */
So when you are compiling with fortify switch on, wur gets defined as
__attribute_warn_unused_result. This attribute essentially tells the compiler to throw out warning if return value of function is not used.
You can redirect your stdout and stderr to /dev/null
gcc -c file.c > /dev/null 2>&1
and get rid of the warning (though even error is redirected to null) or you can redirect to file as
gcc -c warn.c > xx 2>&1
Hope this helps . And like said above, always check the value of "write", the error will automatically vanish
:)
I stumbled over an interesting question in a forum a long time ago and I want to know the answer.
Consider the following C function:
f1.c
#include <stdbool.h>
bool f1()
{
int var1 = 1000;
int var2 = 2000;
int var3 = var1 + var2;
return (var3 == 0) ? true : false;
}
This should always return false since var3 == 3000. The main function looks like this:
main.c
#include <stdio.h>
#include <stdbool.h>
int main()
{
printf( f1() == true ? "true\n" : "false\n");
if( f1() )
{
printf("executed\n");
}
return 0;
}
Since f1() should always return false, one would expect the program to print only one false to the screen. But after compiling and running it, executed is also displayed:
$ gcc main.c f1.c -o test
$ ./test
false
executed
Why is that? Does this code have some sort of undefined behavior?
Note: I compiled it with gcc (Ubuntu 4.9.2-10ubuntu13) 4.9.2.
As noted in other answers, the problem is that you use gcc with no compiler options set. If you do this, it defaults to what is called "gnu90", which is a non-standard implementation of the old, withdrawn C90 standard from 1990.
In the old C90 standard there was a major flaw in the C language: if you didn't declare a prototype before using a function, it would default to int func () (where ( ) means "accept any parameter"). This changes the calling convention of the function func, but it doesn't change the actual function definition. Since the size of bool and int are different, your code invokes undefined behavior when the function is called.
This dangerous nonsense behavior was fixed in the year 1999, with the release of the C99 standard. Implicit function declarations were banned.
Unfortunately, GCC up to version 5.x.x still uses the old C standard by default. There is probably no reason why you should want to compile your code as anything but standard C. So you have to explicitly tell GCC that it should compile your code as modern C code, instead of some 25+ years old, non-standard GNU crap.
Fix the problem by always compiling your program as:
gcc -std=c11 -pedantic-errors -Wall -Wextra
-std=c11 tells it to make a half-hearted attempt to compile according the (current) C standard (informally known as C11).
-pedantic-errors tells it to whole-heartedly do the above, and give compiler errors when you write incorrect code which violates the C standard.
-Wall means give me some extra warnings that might be good to have.
-Wextra means give me some other extra warnings that might be good to have.
You don't have a prototype declared for f1() in main.c, so it is implicitly defined as int f1(), meaning it is a function that takes an unknown number of arguments and returns an int.
If int and bool are of different sizes, this will result in undefined behavior. For example, on my machine, int is 4 bytes and bool is one byte. Since the function is defined to return bool, it puts one byte on the stack when it returns. However, since it's implicitly declared to return int from main.c, the calling function will try to read 4 bytes from the stack.
The default compilers options in gcc won't tell you that it's doing this. But if you compile with -Wall -Wextra, you'll get this:
main.c: In function ‘main’:
main.c:6: warning: implicit declaration of function ‘f1’
To fix this, add a declaration for f1 in main.c, before main:
bool f1(void);
Note that the argument list is explicitly set to void, which tells the compiler the function takes no arguments, as opposed to an empty parameter list which means an unknown number of arguments. The definition f1 in f1.c should also be changed to reflect this.
I think it's interesting to see where the size-mismatch mentioned in Lundin's excellent answer actually happens.
If you compile with --save-temps, you will get assembly files that you can look at. Here's the part where f1() does the == 0 comparison and returns its value:
cmpl $0, -4(%rbp)
sete %al
The returning part is sete %al. In C's x86 calling conventions, return values 4 bytes or smaller (which includes int and bool) are returned via register %eax. %al is the lowest byte of %eax. So, the upper 3 bytes of %eax are left in an uncontrolled state.
Now in main():
call f1
testl %eax, %eax
je .L2
This checks whether the whole of %eax is zero, because it thinks it's testing an int.
Adding an explicit function declaration changes main() to:
call f1
testb %al, %al
je .L2
which is what we want.
Please compile with a command such as this one:
gcc -Wall -Wextra -Werror -std=gnu99 -o main.exe main.c
Output:
main.c: In function 'main':
main.c:14:5: error: implicit declaration of function 'f1' [-Werror=impl
icit-function-declaration]
printf( f1() == true ? "true\n" : "false\n");
^
cc1.exe: all warnings being treated as errors
With such a message, you should know what to do to correct it.
Edit: After reading a (now deleted) comment, I tried to compile your code without the flags. Well, This led me to linker errors with no compiler warnings instead of compiler errors. And those linker errors are more difficult to understand, so even if -std-gnu99 is not necessary, please try to allways use at least -Wall -Werror it will save you a lot of pain in the ass.
I am using C99 under GCC.
I have a function declared static inline in a header that I cannot modify.
The function never returns but is not marked __attribute__((noreturn)).
How can I call the function in a way that tells the compiler it will not return?
I am calling it from my own noreturn function, and partly want to suppress the "noreturn function returns" warning but also want to help the optimizer etc.
I have tried including a declaration with the attribute but get a warning about the repeated declaration.
I have tried creating a function pointer and applying the attribute to that, but it says the function attribute cannot apply to a pointed function.
From the function you defined, and which calls the external function, add a call to __builtin_unreachable which is built into at least GCC and Clang compilers and is marked noreturn. In fact, this function does nothing else and should not be called. It's only here so that the compiler can infer that program execution will stop at this point.
static inline external_function() // lacks the noreturn attribute
{ /* does not return */ }
__attribute__((noreturn)) void your_function() {
external_function(); // the compiler thinks execution may continue ...
__builtin_unreachable(); // ... and now it knows it won't go beyond here
}
Edit: Just to clarify a few points raised in the comments, and generally give a bit of context:
A function has has only two ways of not returning: loop forever, or short-circuit the usual control-flow (e.g. throw an exception, jump out of the function, terminate the process, etc.)
In some cases, the compiler may be able to infer and prove through static analysis that a function will not return. Even theoretically, this is not always possible, and since we want compilers to be fast only obvious/easy cases are detected.
__attribute__((noreturn)) is an annotation (like const) which is a way for the programmer to inform the compiler that he's absolutely sure a function will not return. Following the trust but verify principle, the compiler tries to prove that the function does indeed not return. If may then issue an error if it proves the function may return, or a warning if it was not able to prove whether the function returns or not.
__builtin_unreachable has undefined behaviour because it is not meant to be called. It's only meant to help the compiler's static analysis. Indeed the compiler knows that this function does not return, so any following code is provably unreachable (except through a jump).
Once the compiler has established (either by itself, or with the programmer's help) that some code is unreachable, it may use this information to do optimizations like these:
Remove the boilerplate code used to return from a function to its caller, if the function never returns
Propagate the unreachability information, i.e. if the only execution path to a code points is through unreachable code, then this point is also unreachable. Examples:
if a function does not return, any code following its call and not reachable through jumps is also unreachable. Example: code following __builtin_unreachable() is unreachable.
in particular, it the only path to a function's return is through unreachable code, the function can be marked noreturn. That's what happens for your_function.
any memory location / variable only used in unreachable code is not needed, therefore settings/computing the content of such data is not needed.
any computations which is probably (1) unnecessary (previous bullet) and (2) has no side effects (such as pure functions) may be removed.
Illustration:
The call to external_function cannot be removed because it might have side-effects. In fact, it probably has at least the side effect of terminating the process!
The return boiler plate of your_function may be removed
Here's another example showing how code before the unreachable point may be removed
int compute(int) __attribute((pure)) { return /* expensive compute */ }
if(condition) {
int x = compute(input); // (1) no side effect => keep if x is used
// (8) x is not used => remove
printf("hello "); // (2) reachable + side effect => keep
your_function(); // (3) reachable + side effect => keep
// (4) unreachable beyond this point
printf("word!\n"); // (5) unreachable => remove
printf("%d\n", x); // (6) unreachable => remove
// (7) mark 'x' as unused
} else {
// follows unreachable code, but can jump here
// from reachable code, so this is reachable
do_stuff(); // keep
}
Several solutions:
redeclaring your function with the __attribute__
You should try to modify that function in its header by adding __attribute__((noreturn)) to it.
You can redeclare some functions with new attribute, as this stupid test demonstrates (adding an attribute to fopen) :
#include <stdio.h>
extern FILE *fopen (const char *__restrict __filename,
const char *__restrict __modes)
__attribute__ ((warning ("fopen is used")));
void
show_map_without_care (void)
{
FILE *f = fopen ("/proc/self/maps", "r");
do
{
char lin[64];
fgets (lin, sizeof (lin), f);
fputs (lin, stdout);
}
while (!feof (f));
fclose (f);
}
overriding with a macro
At last, you could define a macro like
#define func(A) {func(A); __builtin_unreachable();}
(this uses the fact that inside a macro, the macro name is not macro-expanded).
If your never-returning func is declaring as returning e.g. int you'll use a statement expression like
#define func(A) ({func(A); __builtin_unreachable(); (int)0; })
Macro-based solutions like above won't always work, e.g. if func is passed as a function pointer, or simply if some guy codes (func)(1) which is legal but ugly.
redeclaring a static inline with the noreturn attribute
And the following example:
// file ex.c
// declare exit without any standard header
void exit (int);
// define myexit as a static inline
static inline void
myexit (int c)
{
exit (c);
}
// redeclare it as notreturn
static inline void myexit (int c) __attribute__ ((noreturn));
int
foo (int *p)
{
if (!p)
myexit (1);
if (p)
return *p + 2;
return 0;
}
when compiled with GCC 4.9 (from Debian/Sid/x86-64) as gcc -S -fverbose-asm -O2 ex.c) gives an assembly file containing the expected optimization:
.type foo, #function
foo:
.LFB1:
.cfi_startproc
testq %rdi, %rdi # p
je .L5 #,
movl (%rdi), %eax # *p_2(D), *p_2(D)
addl $2, %eax #, D.1768
ret
.L5:
pushq %rax #
.cfi_def_cfa_offset 16
movb $1, %dil #,
call exit #
.cfi_endproc
.LFE1:
.size foo, .-foo
You could play with #pragma GCC diagnostic to selectively disable a warning.
Customizing GCC with MELT
Finally, you could customize your recent gcc using the MELT plugin and coding your simple extension (in the MELT domain specific language) to add the attribute noreturn when encoutering the desired function. It is probably a dozen of MELT lines, using register_finish_decl_first and a match on the function name.
Since I am the main author of MELT (free software GPLv3+) I could perhaps even code that for you if you ask, e.g. here or preferably on gcc-melt#googlegroups.com; give the concrete name of your never-returning function.
Probably the MELT code is looking like:
;;file your_melt_mode.melt
(module_is_gpl_compatible "GPLv3+")
(defun my_finish_decl (decl)
(let ( (tdecl (unbox :tree decl))
)
(match tdecl
(?(tree_function_decl_named
?(tree_identifier ?(cstring_same "your_function_name")))
;;; code to add the noreturn attribute
;;; ....
))))
(register_finish_decl_first my_finish_decl)
The real MELT code is slightly more complex. You want to define your_adding_attr_mode there. Ask me for more.
Once you coded your MELT extension your_melt_mode.melt for your needs (and compiled that MELT extension into your_melt_mode.quicklybuilt.so as documented in the MELT tutorials) you'll compile your code with
gcc -fplugin=melt \
-fplugin-arg-melt-extra=your_melt_mode.quicklybuilt \
-fplugin-arg-melt-mode=your_adding_attr_mode \
-O2 -I/your/include -c yourfile.c
In other words, you just add a few -fplugin-* flags to your CFLAGS in your Makefile !
BTW, I'm just coding in the MELT monitor (on github: https://github.com/bstarynk/melt-monitor ..., file meltmom-process.melt something quite similar.
With a MELT extension, you won't get any additional warning, since the MELT extension would alter the internal GCC AST (a GCC Tree) of the declared function on the fly!
Customizing GCC with MELT is probably the most bullet-proof solution, since it is modifying the GCC internal AST. Of course, it is probably the most costly solution (and it is GCC specific and might need -small- changes when GCC is evolving, e.g. when using the next version of GCC), but as I am trying to show it is quite easy in your case.
PS. In 2019, GCC MELT is an abandoned project. If you want to customize GCC (for any recent version of GCC, e.g. GCC 7, 8, or 9), you need to write your own GCC plugin in C++.
In C, does initialising a variable to it's own value make sense? If yes, what for?
Allow me to elaborate. In Git sources there are some examples of initialising a variable to it's own undefined value, as seen in transport.c or wt-status.c. I removed assignments from those declarations and run tests. Seeing no regressions, I thought that those assignments were redundant.
On the other hand, I did some simple tests with GCC 4.6 and Clang 2.9.
#include <stdio.h>
int main() {
printf("print to increase probability of registers being non-zero\n");
int status = status;
return printf("%i\n", status);
}
Compiling with -Wall -std=c99 and various -O levels prints no warnings and shows that status == 0. Clang with a non-zero optimisation level prints some garbage values though. It makes me infer that results of such expressions are undefined.
I can imagine that such assignment can suppress an uninitialised variable warning, but it's not the case for the examples taken from Git. Removing assignments doesn't introduce any warnings.
Are such assignments an undefined behaviour? If not, what do you use them for?
I've suggested the change on the Git mailing list. Here's what I've learned.
This compiles because Standard C99 §6.2.1/7 says:
Any identifier that is not a structure, union, or enumeration tag "has scope that begins just after the completion of its declarator." The declarator is followed by the initializer.
However, value of status is Indeterminate. And you cannot rely on it being initialized to something meaningful.
How does it work?
int status creates an space for the variable to exist on the stack(local storage) which is then further read to perform status = status, status might get initialized to any value that was present in the stack frame.
How can you guard against such self Initialization?
gcc provides a specific setting to detect self Initializations and report them as errors:
-Werror=uninitialized -Winit-self
Why is it used in this code?
The only reason I can think it is being used in the said code is to suppress the unused variable warning for ex: In transport.c, if the control never goes inside the while loop then in that control flow cmp will be unused and the compiler must be generating a warning for it. Same scenario seems to be with status variable in wt-status.c
For me the only reason of such self-assigning initialization is to avoid a warning.
In the case of your transport.c, I don't even understand why it is useful. I would have left cmp uninitialized.
My own habit (at least in C) is to initialize all the variables, usually to 0. The compiler will optimize unneeded initialization, and having all variables initialized makes debugging easier.
There is a case when I want a variable to remain uninitialized, and I might self-assign it: random seeds:
unsigned myseed = myseed;
srand(myseed);
On MacOS X 10.7.2, I tried this example - with the result shown...
$ cat x3.c
#include <stdio.h>
int status = -7;
int main()
{
printf("status = %d\n", status);
int status = status;
printf("status = %d\n", status);
return 0;
}
$ make x3
gcc -O -std=c99 -Wall -Wextra x3.c -o x3
$ ./x3
status = -7
status = 1787486824
$
The stack space where the local status in main() has been used by printf() so the self-initialization copies garbage around.
I think status = status doesn't change the value of status (compared to int status;). I think it is used to suppress the unused variable warning.