int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
...open()... O_CREAT flag...
What if that mode is not specified?
In addition to other people's answers, if you want to future-proof against this by getting a compiler error when you forgot to specify the mode flag in those cases where it is needed(ie. O_CREAT or O_TMPFILE, according to man 2 open
), you'd have to use the GNU project C and C++ compiler (eg. the gcc
command) with arg. -D_FORTIFY_SOURCE=1
(or 2
) and an optimization flag eg. -O1
or the usual -O2
(because _FORTIFY_SOURCE requires compiling with optimization (-O)
).
For example:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
int file;
static const char filename[] = "test.test";
if ((file = open(filename, O_RDWR | O_CREAT | O_TRUNC)) == 1)
{
perror("Error opening file.");
exit(EXIT_FAILURE);
}
close(file);
}
(save that as file: a.c
)
$ gcc -D_FORTIFY_SOURCE=2 -O1 a.c
In file included from /usr/include/fcntl.h:328,
from a.c:3:
In function ‘open’,
inlined from ‘main’ at a.c:10:13:
/usr/include/bits/fcntl2.h:50:4: error: call to ‘__open_missing_mode’ declared with attribute error: open with O_CREAT or O_TMPFILE in second argument needs 3 arguments
__open_missing_mode ();
^~~~~~~~~~~~~~~~~~~~~~
So you get: open with O_CREAT or O_TMPFILE in second argument needs 3 arguments
Pedantics follow:
Note that: -O0
or no -O
arg. won't work(ie. it won't tell you that you forgot to add mode
because it's as if _FORTIFY_SOURCE
was unspecified or simply ignored):
$ gcc -D_FORTIFY_SOURCE=2 -O0 a.c
In file included from /usr/include/bits/libc-header-start.h:33,
from /usr/include/stdio.h:27,
from a.c:1:
/usr/include/features.h:382:4: warning: #warning _FORTIFY_SOURCE requires compiling with optimization (-O) [-Wcpp]
# warning _FORTIFY_SOURCE requires compiling with optimization (-O)
^~~~~~~
Using _FORTIFY_SOURCE
will protect you like that for other cases too, and for open()
there is one more case: open can be called either with 2 or 3 arguments, not more
, seen in file /usr/include/bits/fcntl2.h
as:
__errordecl (__open_too_many_args,
"open can be called either with 2 or 3 arguments, not more");
__errordecl (__open_missing_mode,
"open with O_CREAT or O_TMPFILE in second argument needs 3 arguments");
__fortify_function int
open (const char *__path, int __oflag, ...)
{
if (__va_arg_pack_len () > 1)
__open_too_many_args ();
if (__builtin_constant_p (__oflag))
{
if (__OPEN_NEEDS_MODE (__oflag) && __va_arg_pack_len () < 1)
{
__open_missing_mode ();
return __open_2 (__path, __oflag);
}
return __open_alias (__path, __oflag, __va_arg_pack ());
}
if (__va_arg_pack_len () < 1)
return __open_2 (__path, __oflag);
return __open_alias (__path, __oflag, __va_arg_pack ());
}
The reason GNU C Compiler (eg. gcc
) is needed is, at the very least, because of the following code from file /usr/include/sys/cdefs.h
:
#if __GNUC_PREREQ (4,3)
# define __warndecl(name, msg) \
extern void name (void) __attribute__((__warning__ (msg)))
# define __warnattr(msg) __attribute__((__warning__ (msg)))
# define __errordecl(name, msg) \
extern void name (void) __attribute__((__error__ (msg)))
#else
# define __warndecl(name, msg) extern void name (void)
# define __warnattr(msg)
# define __errordecl(name, msg) extern void name (void)
#endif
that says that gcc version 4.3 is the minimum required for this to work.
(FYI: my current version is gcc (GCC) 8.3.0)
So if you try clang
version 8.0.0 (tags/RELEASE_800/final) Target: x86_64-pc-linux-gnu, you don't get the compile error:
$ clang -D_FORTIFY_SOURCE=2 -O1 a.c
(no output here even with -, compilation succeeded: a.out created)
because this clang version defines __GNUC__
to be 4
and __GNUC_MINOR__
to be 2
thus 4.2 is just shy of the 4.3 required for it to work; and forcing eg. 8.3 won't work:
$ clang -D_FORTIFY_SOURCE=1 -D__GNUC__=8 -D__GNUC_MINOR__=8 -O1 a.c
In file included from <built-in>:355:
<command line>:2:9: warning: '__GNUC__' macro redefined [-Wmacro-redefined]
#define __GNUC__ 8
^
<built-in>:9:9: note: previous definition is here
#define __GNUC__ 4
^
In file included from <built-in>:355:
<command line>:3:9: warning: '__GNUC_MINOR__' macro redefined [-Wmacro-redefined]
#define __GNUC_MINOR__ 8
^
<built-in>:7:9: note: previous definition is here
#define __GNUC_MINOR__ 2
^
2 warnings generated.
The above source codes come from glibc
2.29.9000.r269.g1f50f2ad85-1 package on Arch Linux. ie.
/usr/include/sys/cdefs.h is owned by glibc 2.29.9000.r269.g1f50f2ad85-1
/usr/include/bits/fcntl2.h is owned by glibc 2.29.9000.r269.g1f50f2ad85-1
PS: without _FORTIFY_SOURCE
, you can get random modes on each program run, like I did:
$ ./go
-r-x--s--T 1 user user 0 May 17 17:22 /tmp/broken_perms.log
$ ./go
---sr-s--- 1 user user 0 May 17 17:23 /tmp/broken_perms.log
$ ./go
-rws--x--- 1 user user 0 May 17 17:23 /tmp/broken_perms.log
$ ./go
--wsr-x--T 1 user user 0 May 17 17:23 /tmp/broken_perms.log