guaranteels voidvoid main是什么意思思

君,已阅读到文档的结尾了呢~~
扫扫二维码,随身浏览文档
手机或平板扫扫即可继续访问
毕业设计-基于STC89C52单片机的交通灯设计
举报该文档为侵权文档。
举报该文档含有违规或不良信息。
反馈该文档无法正常浏览。
举报该文档为重复文档。
推荐理由:
将文档分享至:
分享完整地址
文档地址:
粘贴到BBS或博客
flash地址:
支持嵌入FLASH地址的网站使用
html代码:
&embed src='/DocinViewer-4.swf' width='100%' height='600' type=application/x-shockwave-flash ALLOWFULLSCREEN='true' ALLOWSCRIPTACCESS='always'&&/embed&
450px*300px480px*400px650px*490px
支持嵌入HTML代码的网站使用
您的内容已经提交成功
您所提交的内容需要审核后才能发布,请您等待!
3秒自动关闭窗口This document is available in:
& & & & & & & &
Fr&d&ric Raynal, Christophe Blaess, Christophe Grenier
&pappy(at)users.sourceforge.net, ccb(at)club-internet.fr, grenier(at)nef.esiea.fr&
About the author:
Christophe Blaess is an independent aeronautics engineer. He is a
Linux fan and does much of his work on this system. He coordinates
the translation of the man pages as published by the
Linux Documentation Project.Christophe Grenier is a 5th year student at the ESIEA, where he
works as a sysadmin too. He has a passion for computer security.Fr&d&ric Raynal has been using Linux for many years
because it doesn't pollute, doesn't use hormones, MSG or
animal bone-meal... only sweat and craft.
Translated to English by:
Georges Tarbouriech &georges.t(at)linuxfocus.org&
Lorne Bailey [proof read] &sherm_pbody(&
Avoiding security holes when developing an application - Part 5: race conditions
This fifth article of our series is dedicated to security problems related to
multitasking.
A race condition occurs when different processes use the same resource
(file, device, memory) at the same time
and each one "believes" it has exclusive access.
This leads to difficult to detect bugs and also to security holes that can
compromise a system's global security.
_________________ _________________ _________________
Introduction
The general principle defining race conditions is the following : a process wants to
access a system resource exclusively. It checks that the resource is not
already used by another process, then uses it as it pleases.
The race condition occurs when another process tries to use the same resource
in the time-lag between the first process checking that resource and actually taking it over.
The side effects may vary. The classical case in OS theory is the deadlock
of both processes. More often it leads to application
malfunction or even to security holes when a process wrongfully benefits from
the privileges another.
What we previously called a resource can have different aspects.
Most notably the race conditions discovered and corrected in the Linux kernel
itself due to competitive access to memory areas. Here, we will focus on
system applications and we'll deem that the concerned resources are
filesystem nodes. This concerns not only regular files but also direct access to
devices through special entry points from the /dev/ directory.
Most of the time, an attack aiming to compromise system security is done against
Set-UID applications since the attacker can
benefit from the privileges of the owner of the executable file.
However, unlike previously discussed security holes (buffer overflow, format
strings...), race conditions usually don't allow the
execution of "customized" code. Rather, they benefit from
the resources of a program
while it's running.
This type of attack is also aimed at "normal" utilities
(not Set-UID), the cracker lying in ambush for another user,
especially root, to run the concerned application and access its
resources. This is also true for writing to a file
(i.e, ~/.rhost in which the string
"+&+" provides a direct access from any machine without
password), or for reading a confidential file (sensitive commercial data,
personal medical information, password file, private key...)
Unlike the security holes discussed in our previous articles, this security
problem applies to every application and not just to Set-UID utilities
and system servers or daemons.
First example
Let's have a look at the behavior of a Set-UID program that needs to save
data in a file belonging to the user. We could, for instance, consider the
case of a mail transport software like sendmail.
Let's suppose the user can both provide a backup filename and a message to write
into that file, which is plausible under some circumstances. The application must
then check if the file belongs to the person who started the program. It also will
check that the file is not a symlink to a system file. Let's not forget, the
program being Set-UID root, it is allowed to modify any file on the
machine. Accordingly, it will compare the file's owner to its own real UID.
Let's write something like :
/* ex_01.c */
#include &stdio.h&
#include &stdlib.h&
#include &unistd.h&
#include &sys/stat.h&
#include &sys/types.h&
main (int argc, char * argv [])
if (argc != 3) {
fprintf (stderr, "usage : %s file message\n", argv [0]);
exit(EXIT_FAILURE);
if (stat (argv [1], & st) & 0) {
fprintf (stderr, "can't find %s\n", argv [1]);
exit(EXIT_FAILURE);
if (st . st_uid != getuid ()) {
fprintf (stderr, "not the owner of %s \n", argv [1]);
exit(EXIT_FAILURE);
if (! S_ISREG (st . st_mode)) {
fprintf (stderr, "%s is not a normal file\n", argv[1]);
exit(EXIT_FAILURE);
if ((fp = fopen (argv [1], "w")) == NULL) {
fprintf (stderr, "Can't open\n");
exit(EXIT_FAILURE);
fprintf (fp, "%s\n", argv [2]);
fclose (fp);
fprintf (stderr, "Write Ok\n");
exit(EXIT_SUCCESS);
As we explained in our first article, it would be better for a
Set-UID application to temporarily drop its privileges and open the
file using the real UID of the user having called it.
As a matter of fact, the above situation corresponds to a
daemon, providing services to every user. Always running under the
root ID, it would check using the UID instead of its own real
UID. Nevertheless, we'll keep this scheme for now, even if it isn't that realistic,
since it allows us to understand the problem while easily "exploiting" the security
As we can see, the program starts doing all the needed checks, i.e. that
the file exists, that it belongs to the user and that it's a normal file.
Next, it actually opens the file and writes the message. That is where
the security hole lies! Or, more exactly, it's within the lapse of time between the
reading of the file attributes with stat() and its opening with
fopen(). This lapse of time is often extremely short but
an attacker can benefit from it to change the file's characteristics.
To make our attack even easier, let's add a line that causes the
process to sleep
between the two operations, thus having the time to do the job by hand. Let's
change the line 30 (previously empty) and insert :
sleep (20);
Now, let' first, let's make the application
Set-UID root. Let's make, it's very
important, a backup copy of our password file
/etc/shadow :
$ cc ex_01.c -Wall -o ex_01
# cp /etc/shadow /etc/shadow.bak
# chown root.root ex_01
# chmod +s ex_01
$ ls -l ex_01
-rwsrwsr-x 1 root
15454 Jan 30 14:14 ex_01
Everything is ready for the attack. We are in a directory belonging to us. We
have a Set-UID root utility (here
ex_01) holding a security hole, and we feel like replacing the line
concerning root from the
/etc/shadow password file with a line containing an empty password.
First, we create a fic file belonging to us :
$ rm -f fic
$ touch fic
Next, we run our application in the background "to keep the lead". We ask it to
write a string into that file. It checks what it has to, sleeps for a while
before really accessing the file.
$ ./ex_01 fic "root::1:99999:::::" &
The content of the root line comes from the shadow(5)
man page, the most important being the empty second field (no password).
While the process is asleep, we have about 20 seconds to remove the
fic file and replace it with a link
(symbolic or physical, both work) to the /etc/shadow file.
Let's remember, that every user can create a link to a file
in a directory belonging to him even if he can't read the content,
(or in /tmp, as we'll see a bit later). However it isn't possible to create a
copy of such a file, since it would require a full read.
$ rm -f fic
$ ln -s /etc/shadow ./fic
Then we ask the shell to bring the ex_01 process back to the
foreground with the fg command, and wait till it finishes :
./ex_01 fic "root::1:99999:::::"
Voil& ! It's over, the
/etc/shadow file only holds one line indicating root has
no password. You don't believe it ?
# cat /etc/shadow
root::1:99999:::::
Let's finish our experiment by putting the old password file back :
# cp /etc/shadow.bak /etc/shadow
cp: replace `/etc/shadow'? y
Let's be more realistic
We succeeded in exploiting a race condition in a
Set-UID root utility. Of course, this program was very "helpful" waiting
for 20 seconds giving us time to modify the files behind its back.
Within a real application, the race condition only applies for a very short
time. How do we take advantage of that ?
Usually, the cracker relies on a brute force attack, renewing the attempts hundreds,
thousands or ten thousand times, using scripts to automate the sequence.
It's possible to improve the chance of "falling" into the security hole with
various tricks aiming at increasing the lapse of time between the two operations
that the program wrongly considers as atomically linked. The idea is to slow
down the target process to manage the delay preceding the file
modification more easily. Different approaches can help us to reach our goal :
To reduce the priority of the attacked process as much as possible by running
it with the nice&-n&20
To increase the system load, running various processes that do CPU
time consuming loops (like while (1););
The kernel doesn't allow debugging Set-UID programs,
but it's possible to force a pseudo step by step execution sending
SIGSTOP-SIGCONT signal sequences thus allowing to
temporarily lock the process (like with the Ctrl-Z key combination in a
shell) and then restart it when needed.
The method allowing us to benefit from a security hole based in race condition is
boring and repetitive, but it really is usable ! Let's try to find the
most effective solutions.
Possible improvement
The problem discussed above relies on the ability to change an object's
characteristics during the time-lapse between two operations,
the whole thing being as continuous as possible. In the previous
situation, the change did not concern the file itself. By the way, as a normal
user it would have been quite difficult to modify, or even to read, the
/etc/shadow file. As a matter of fact, the change relies on the
link between the existing file node in the name tree and the file itself as a
physical entity. Let's remember most of the system commands
(rm, mv, ln, etc.) act on the file name
not on the file content. Even when you delete a file (using rm and
the unlink() system call), the content is really deleted when the
last physical link - the last reference - is removed.
The mistake made in the previous program is considering
the association between the name of the file and its content as unchangeable,
or at least constant, during the lapse of time between stat() and
fopen() operation.
Thus, the example of a physical link should suffice to verify that this association is
not a permanent one at all. Let's take an example using this type of link. In a
directory belonging to us, we create a new link to a system file. Of course, the file's
owner and the access mode are kept. The ln command -f
option forces the creation, even if that name already exists :
$ ln -f /etc/fstab ./myfile
$ ls -il /etc/fstab myfile
8570 -rw-r--r--
716 Jan 25 19:07 /etc/fstab
8570 -rw-r--r--
716 Jan 25 19:07 myfile
$ cat myfile
defaults,mand
/mnt/floppy
noauto,user
/mnt/cdrom
iso9660 noauto,ro,user
noauto,user
/mnt/audio
noauto,user
/home/ccb/annexe
noauto,user
gid=5,mode=620
$ ln -f /etc/host.conf ./myfile
$ ls -il /etc/host.conf myfile
8198 -rw-r--r--
2000 /etc/host.conf
8198 -rw-r--r--
2000 myfile
$ cat myfile
order hosts,bind
The /bin/ls -i option displays the inode
number at the beginning of the line. We can see the same name points to two
different physical inodes.
In fact, we would like the functions that check and access the file to
always point to the same content and the same inode. And it's possible !
The kernel itself automatically manages this association when it provides us
with a file descriptor. When we open a file for reading, the open()
system call returns an integer value, that is the descriptor, associating it with
the physical file by an internal table. All the reading we'll do next will
concern this file content, no matter what happens to the name used during the file
open operation.
Let's emphasize that point : once a file has been opened, every operation
on the filename, including removing it, will have no effect on the file
content. As long as there is still a process holding a descriptor for a file, the
file content isn't removed from the disk, even if its name disappears from the
directory where it was stored. The kernel maintains the association to
the file content between the open() system
call providing a file descriptor and the release of this descriptor by
close() or the process ends.
So there we have our solution ! We can open the file and then check
the permissions by examining the descriptor characteristics instead of the filename
ones. This is done using the fstat() system call (this last working
like stat()), but checking a file descriptor rather than a path.
To access the content of the file using the descriptor we'll use the fdopen()
function (that works like fopen()) while relying on a descriptor
rather than on a filename. Thus, the program becomes :
/* ex_02.c */
#include &fcntl.h&
#include &stdio.h&
#include &stdlib.h&
#include &unistd.h&
#include &sys/stat.h&
#include &sys/types.h&
main (int argc, char * argv [])
if (argc != 3) {
fprintf (stderr, "usage : %s file message\n", argv [0]);
exit(EXIT_FAILURE);
if ((fd = open (argv [1], O_WRONLY, 0)) & 0) {
fprintf (stderr, "Can't open %s\n", argv [1]);
exit(EXIT_FAILURE);
fstat (fd, & st);
if (st . st_uid != getuid ()) {
fprintf (stderr, "%s not owner !\n", argv [1]);
exit(EXIT_FAILURE);
if (! S_ISREG (st . st_mode)) {
fprintf (stderr, "%s not a normal file\n", argv[1]);
exit(EXIT_FAILURE);
if ((fp = fdopen (fd, "w")) == NULL) {
fprintf (stderr, "Can't open\n");
exit(EXIT_FAILURE);
fprintf (fp, "%s", argv [2]);
fclose (fp);
fprintf (stderr, "Write Ok\n");
exit(EXIT_SUCCESS);
This time, after line 20, no change to the filename (deleting, renaming,
linking) will affect our program' the content of the original
physical file will be kept.
Guidelines
When manipulating a file it's important to ensure the association between
the internal representation and the real content stays constant.
Preferably, we'll use the following system calls to manipulate the
physical file as an already open descriptor rather than their equivalents using
the path to the file :
System call
fchdir (int fd)
Goes to the directory represented by fd.
fchmod (int fd, mode_t mode)
Changes the file access rights.
fchown (int fd, uid_t uid, gid_t gif)
Changes the file owner.
fstat (int fd, struct stat * st)
Consults the informations stored within the inode of the physical file.
ftruncate (int fd, off_t length)
Truncates an existing file.
fdopen (int fd, char * mode)
Initializes IO from an already open descriptor. It's an stdio
library routine, not a system call.
Then, of course, you must open the file in the wanted mode, calling
open() (don't forget the third argument when creating a new file).
More on open() later when we discuss the temporary file
We must insist that it is important to check the system calls return codes.
For instance, let's mention, even if it has nothing to do with
race conditions, a problem found in old /bin/login
implementations because it neglected an error code check. This application,
automatically provided a root access when not finding the
/etc/passwd file. This behavior can seem acceptable as soon as a
damaged file system repair is concerned. On the other hand, checking that it was
impossible to open the file instead of checking if the file really existed, was
less acceptable. Calling /bin/login after opening the
maximum number of allowed descriptors allowed any user to get
root access... Let's finish with this digression insisting in how it's
important to check, not only the system call's success or failure, but the error
codes too, before taking any action about system security.
Race conditions to the file content
A program dealing with system security shouldn't rely on the exclusive
access to a file content. More exactly, it's important to properly manage the
risks of race conditions to the same file. The main danger comes from a user
running multiple instances of a Set-UID root
application simultaneously or establishing multiple connections at once with the same daemon,
hoping to create a race condition situation, during which the content of a system
file could be modified in an unusual way.
To avoid a program being sensitive to this kind of situation, it's necessary to
institute an exclusive access mechanism to the file data. This is the same
problem as the one found in databases when various users are allowed to
simultaneously query or change the content of a file. The principle of file locking
solves this problem.
When a process wants to write into a file, it asks the kernel to lock that file
- or a part of it. As long as the process keeps the lock, no other process can
ask to lock the same file, or at least the same part of the file. In the same
way, a process asks for a lock before reading the file content to
ensure no changes will be made while it holds the lock.
As a matter of fact, the system is more clever than that : the kernel distinguishes
between the locks required for file reading and those for file
writing. Various processes can hold a lock for reading simultaneously
since no one will attempt to change the file content. However, only one process
can hold a lock for writing at a given time, and no other lock can be
provided at the same time, even for reading.
There are two types of locks (mostly incompatible with each other). The first one
comes from BSD and relies on the flock() system call. Its first
argument is the descriptor of the file you wish to access in an exclusive way,
and the second one is a symbolic constant representing the operation to be done.
It can have different values : LOCK_SH (lock for reading),
LOCK_EX (for writing), LOCK_UN (release of the lock).
The system call blocks as long as the requested operation remains
impossible. However, you can do a binary OR | of the
LOCK_NB constant for the call to fail instead of staying locked.
The second type of lock comes from System V, and relies on the
fcntl() system call whose invocation is a bit complicated.
There's a library function called lockf() close to the system call
but not as fast. fcntl()'s first argument is the descriptor
of the file to lock. The second one represents the operation to be performed :
F_SETLK and F_SETLKW manage a lock, the second command
stays blocks till the operation becomes possible, while the first immediately
returns in case of failure.
F_GETLK consults the lock state of a file (which is useless
for current applications). The third argument is a pointer to a variable of
struct flock type, describing the lock.
The flock structure important members are the following :
Expected action :
F_RDLCK (to lock for reading),
F_WRLCK (to lock for writing) and
F_UNLCK (to release the lock).
l_start Field origin (usually
SEEK_SET).
Position of the beginning of the lock (usually 0).
Length of the lock, 0 to reach the end of the file.
We can see fcntl() can lock limited portions of the file, but it's
able to do much more compared to flock(). Let's have a look at a
small program asking for a lock for reading concerning files which names are
given as an argument, and waiting for the user to press the Enter key before
finishing (and thus releasing the locks).
/* ex_03.c */
#include &fcntl.h&
#include &stdio.h&
#include &stdlib.h&
#include &sys/stat.h&
#include &sys/types.h&
#include &unistd.h&
main (int argc, char * argv [])
char buffer [2];
for (i = 1; i & i ++) {
fd = open (argv [i], O_RDWR | O_CREAT, 0644);
if (fd & 0) {
fprintf (stderr, "Can't open %s\n", argv [i]);
exit(EXIT_FAILURE);
lock . l_type = F_WRLCK;
lock . l_whence = SEEK_SET;
lock . l_start = 0;
lock . l_len = 0;
if (fcntl (fd, F_SETLK, & lock) & 0) {
fprintf (stderr, "Can't lock %s\n", argv [i]);
exit(EXIT_FAILURE);
fprintf (stdout, "Press Enter to release the lock(s)\n");
fgets (buffer, 2, stdin);
exit(EXIT_SUCCESS);
We first launch this program from a first console where it waits :
$ cc -Wall ex_03.c -o ex_03
$ ./ex_03 myfile
Press Enter to release the lock(s)
&From another terminal...
&&&&$ ./ex_03 myfile
&&&&Can't lock myfile
Pressing Enter in the first console, we release the locks.
With this locking mechanism, you can prevent race conditions to directories
and print queues, like the lpd daemon, using a
flock() lock on the /var/lock/subsys/lpd file,
thus allowing only one instance.
You can also manage the access to a system file in a secure way like
/etc/passwd, locked using fcntl() from the
pam library when changing a user's data.
However, this only protects from interferences with applications having
correct behavior, that is, asking the kernel to reserve the proper access before
reading or writing to an important system file. We now talk about cooperative
lock, what shows the application liability towards data access. Unfortunately, a
badly written program is able to replace file content, even if another
process, with good behavior, has a lock for writing. Here is an example. We
write a few letters into a file and lock it using the previous program :
$ echo "FIRST" & myfile
$ ./ex_03 myfile
Press Enter to release the lock(s)
&From another console, we can change the file :
&&&&$ echo "SECOND" & myfile
Back to the first console, we check the "damages" :
$ cat myfile
To solve this problem, the Linux kernel provides the sysadmin with a locking
mechanism coming from System V. Therefore you can only use it with
fcntl() locks and not with flock().
The administrator can tell the kernel the fcntl() locks are
strict, using a particular combination of access rights.
Then, if a process locks a file for writing, another process won't be able to
write into that file (even as root).
The particular combination is to use the Set-GID bit while the
execution bit is removed for the group. This is obtained with the command :
$ chmod g+s-x myfile
However this is not enough. For a file to automatically benefit from strict
cooperative locks, the mandatory attribute must be activated on the
partition where it can be found. Usually, you have to change the
/etc/fstab file to add the mand option in the 4th
column, or typing the command :
/dev/hda5 on / type ext2 (rw)
# mount / -o remount,mand
/dev/hda5 on / type ext2 (rw,mand)
Now, we can check that a change from another console is impossible :
$ ./ex_03 myfile
Press Enter to release the lock(s)
&From another terminal :
&&&&$ echo "THIRD" & myfile
&&&&bash: myfile: Resource temporarily not available
And back to the first console :
$ cat myfile
The administrator and not the programmer has to decide to make strict file locks
(for instance /etc/passwd, or /etc/shadow). The
programmer has to control the way the data is accessed, what ensures his
application to manages data coherently when reading and it is not dangerous for other
processes when writing, as long as the environment is properly administrated.
Temporary files
Very often a program needs to temporarily store data in an external file. The
most usual case is inserting a record in the middle of a sequential ordered
file, which implies that we make a copy of the original file in a temporary file,
while adding new information. Next the unlink() system call removes
the original file and rename() renames the temporary file to
replace the previous one.
Opening a temporary file, if not done properly, is often the starting point of
race condition situations for an ill-intentioned user. Security holes based on
the temporary files have been recently discovered in applications such as
Apache, Linuxconf, getty_ps, wu-ftpd,
rdist, gpm, inn, etc.
Let's remember a few principles to avoid this sort of trouble.
Usually, temporary file creation is done in the /tmp directory.
This allows the sysadmin to know where short term data storage is done. Thus, it's
also possible to program a periodic cleaning (using cron), the use
of an independent partition formated at boot time, etc.
Usually, the administrator defines the location reserved for temporary files in
the &paths.h& and &stdio.h& files, in the
_PATH_TMP and P_tmpdir symbolic constants definition.
As a matter of fact, using another default directory than /tmp is
not that good, since it would imply recompiling every application, including the
C library. However, let's mention that GlibC routine behavior can be defined
using the TMPDIR environment variable.
Thus, the user can ask the temporary files to be stored in a directory belonging
to him rather than in /tmp. This is sometimes mandatory when the
partition dedicated to /tmp is too small to run applications
requiring big amount of temporary storage.
The /tmp system directory is something special because of its
access rights :
$ ls -ld /tmp
drwxrwxrwt 7 root
31744 Feb 14 09:47 /tmp
The Sticky-Bit represented by the letter t at the end or
the 01000 octal mode, has a particular meaning when applied to a directory :
only the directory owner (root ), and the owner of a file found in that
directory are able to delete the file. The directory having a full write access,
each user can put his files in it, being sure they are protected - at least till
the next clean up managed by the sysadmin.
Nevertheless, using the temporary storage directory may cause a few problems.
Let's start with the trivial case, a Set-UID root application talking
to a user. Let's talk about a mail transport program. If this process receives
a signal asking it to finish immediately, for instance
SIGTERM or SIGQUIT during a system shutdown, it can
try to save on the fly the mail already written but not sent. With old versions,
this was done in /tmp/dead.letter. Then, the user just had to
create (since he can write into /tmp) a physical link to /etc/passwd
with the name dead.letter for the mailer
(running under effective UID root) to write to this file the content of
the not yet finished mail (incidently containing a line
"root::1:99999:::::").
The first problem with this behavior is the foreseeable nature of the filename.
You can to watch such an application only once to deduct it will use the
/tmp/dead.letter file name. Therefore, the first step is to use a
filename defined for the current program instance. There are various library
functions able to provide us with a personal temporary filename.
Let's suppose we have such a function providing a unique name for our temporary
file. Free software being available with source code (and so for C library), the
filename is however foreseeable even if it's rather difficult. An attacker could
create a symlink to the name provided by the C library. Our first reaction is to
check the file exists before opening it. Naively we could write something like :
if ((fd = open (filename, O_RDWR)) != -1) {
fprintf (stderr, "%s already exists\n", filename);
exit(EXIT_FAILURE);
fd = open (filename, O_RDWR | O_CREAT, 0644);
Obviously, this is a typical case of race condition,
where a security hole opens following the action from a user succeeding in
creating a link to /etc/passwd between the first
open() and the second one.
These two operations have to be done in an atomic way, without any manipulation
able to take place between them. This is possible using a specific option of the
open() system call. Called O_EXCL, and used
in conjunction with O_CREAT, this option makes the
open() fail if the file already exists, but the check of existence is
atomically linked to the creation.
By the way, the 'x' Gnu extension for the opening modes of the
fopen() function, requires an exclusive file creation, failing if
the file already exists :
if ((fp = fopen (filename, "r+x")) == NULL) {
perror ("Can't create the file.");
exit (EXIT_FAILURE);
The temporary files permissions are quite important too. If you have to write
confidential information into a mode 644 file (read/write for the
owner, read only for the rest of the world) it can be a bit of a nuisance. The
#include &sys/types.h&
#include &sys/stat.h&
mode_t umask(mode_t mask);
function allows us to determine the permissions of a file at creation time. Thus,
following a umask(077) call, the file will be open in mode 600
(read/write for the owner, no rights at all for the others).
Usually, the temporary file creation is done in three steps :
unique name creation (random)&;
file opening using O_CREAT | O_EXCL, with the most
checking the result when opening the file and reacting accordingly (either
retry or quit).
How create a temporary file ? The
#include &stdio.h&
char *tmpnam(char *s);
char *tempnam(const char *dir, const char *prefix);
functions return pointers to randomly created names.
The first function accepts a NULL argument, then it returns a
static buffer address. Its content will change at
tmpnam(NULL) next call. If the argument is an allocated string, the
name is copied there, what requires a string of at least
L-tmpnam bytes. Be careful with buffer overflows !
The man page informs about problems when the function is used with
a NULL parameter, if _POSIX_THREADS or
_POSIX_THREAD_SAFE_FUNCTIONS are defined.
The tempnam() function returns a pointer to a string. The
dir directory must be "suitable" (the
man page describes the right meaning of "suitable"). This function
checks the file doesn't exist before returning its name. However, once again,
the man page doesn't recommend its use, since "suitable" can have a
different meaning according to the function implementations.
Let's mention that Gnome recommends its use in this way :
filename = tempnam (NULL, "foo");
fd = open (filename, O_CREAT | O_EXCL | O_TRUNC | O_RDWR, 0600);
free (filename);
} while (fd == -1);
The loop used here, reduces the risks but creates new ones. What would happen if
the partition where you want to create the temporary file is full, or if the
system already opened the maximum number of files available at once...
#include &stdio.h&
FILE *tmpfile (void);
function creates an unique filename and opens it. This file is automatically
deleted at closing time.
With GlibC-2.1.3, this function uses a mechanism similar to
tmpnam() to generate the filename, and opens the corresponding
descriptor. The file is then deleted, but Linux really removes it when no
resources at all use it, that is when the file descriptor is released, using a
close() system call.
FILE * fp_
if ((fp_tmp = tmpfile()) == NULL) {
fprintf (stderr, "Can't create a temporary file\n");
exit (EXIT_FAILURE);
/* ... use of the temporary file ... */
fclose (fp_tmp);
/* real deletion from the system */
The simplest cases don't require filename change nor transmission to
another process, but only storage and data re-reading in a temporary area.
We therefore don't need to know the name of the temporary file but only to
access its content.
The tmpfile() function does it.
The man page says nothing, but the Secure-Programs-HOWTO doesn't
recommend it. According to the author, the specifications don't guarantee the
file creation and he hasn't been able to check every implementation. Despite this
reserve, this function is the most efficient.
#include &stdlib.h&
char *mktemp(char *template);
int mkstemp(char *template);
functions create an unique name from a template made of a string ending with
"XXXXXX". These 'X's are replaced to get an unique filename.
According to versions, mktemp() replaces the first five
'X' with the Process ID (PID) ... what makes the name rather easy to
guess : only the last 'X' is random. Some versions allow more than six 'X'.
mkstemp() is the recommended function in the
Secure-Programs-HOWTO. Here is the method :
#include &stdio.h&
#include &stdlib.h&
#include &sys/types.h&
#include &sys/stat.h&
void failure(msg) {
fprintf(stderr, "%s\n", msg);
* Creates a temporary file and returns it.
* This routine removes the filename from the filesystem thus
* it doesn't appear anymore when listing the directory.
FILE *create_tempfile(char *temp_filename_pattern)
mode_t old_
FILE *temp_
/* Create file with restrictive permissions */
old_mode = umask(077);
temp_fd = mkstemp(temp_filename_pattern);
(void) umask(old_mode);
if (temp_fd == -1) {
failure("Couldn't open temporary file");
if (!(temp_file = fdopen(temp_fd, "w+b"))) {
failure("Couldn't create temporary file's file descriptor");
if (unlink(temp_filename_pattern) == -1) {
failure("Couldn't unlink temporary file");
return temp_
These functions show the problems concerning abstraction and portability. That
is, the standard library functions are expected to provide features
(abstraction)... but the way to implement them varies according to the system
(portability).
For instance, the tmpfile() function opens a temporary file
in different ways (some versions don't use O_EXCL),
or mkstemp() handles a variable number of 'X' according to
implementations.
Conclusion
We flew over most of the security problems concerning race conditions to the same
resource. Let's remember you must never assume that two consecutive operations are
always sequentially processed in the CPU unless the kernel manages this. If race conditions generate
security holes, you must not neglect the holes caused by relying on other resources, such
as variables shared between threads or memory segments shared using
shmget(). Selection access mechanisms (semaphore, for
example) must be used to avoid hard to discover bugs.
Secure-Programs-HOWTO by David A. Wheeler&: www.linuxdoc.org/HOWTO/Secure-Programs-HOWTO/
Talkback form for this article
Every article has its own talkback page. On this page you can submit a comment or look at comments from other readers:
Fr&d&ric Raynal, Christophe Blaess, Christophe Grenier,
Translation information:
fr --& -- :
Fr&d&ric Raynal, Christophe Blaess, Christophe Grenier &pappy(at)users.sourceforge.net, ccb(at)club-internet.fr, grenier(at)nef.esiea.fr&
fr --& en: Georges Tarbouriech &georges.t(at)linuxfocus.org&
en --& en: Lorne Bailey &sherm_pbody(&
, generated by lfparser version 2.31

我要回帖

更多关于 c语言void是什么意思 的文章

 

随机推荐