Properly handle shm_open validation. Fixes bug 16274.
[platform/upstream/glibc.git] / sysdeps / unix / sysv / linux / shm_open.c
1 /* Copyright (C) 2000-2013 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, see
16    <http://www.gnu.org/licenses/>.  */
17
18 #include <errno.h>
19 #include <fcntl.h>
20 #include <mntent.h>
21 #include <paths.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <sys/mman.h>
27 #include <sys/statfs.h>
28 #include <bits/libc-lock.h>
29 #include "linux_fsinfo.h"
30
31 #include <kernel-features.h>
32
33
34 /* Mount point of the shared memory filesystem.  */
35 static struct
36 {
37   char *dir;
38   size_t dirlen;
39 } mountpoint;
40
41 /* This is the default directory.  */
42 static const char defaultdir[] = "/dev/shm/";
43
44 /* Protect the `mountpoint' variable above.  */
45 __libc_once_define (static, once);
46
47
48 #if defined O_CLOEXEC && !defined __ASSUME_O_CLOEXEC
49 static bool have_o_cloexec;
50 #endif
51
52
53 /* Determine where the shmfs is mounted (if at all).  */
54 static void
55 where_is_shmfs (void)
56 {
57   char buf[512];
58   struct statfs f;
59   struct mntent resmem;
60   struct mntent *mp;
61   FILE *fp;
62
63   /* The canonical place is /dev/shm.  This is at least what the
64      documentation tells everybody to do.  */
65   if (__statfs (defaultdir, &f) == 0 && f.f_type == SHMFS_SUPER_MAGIC)
66     {
67       /* It is in the normal place.  */
68       mountpoint.dir = (char *) defaultdir;
69       mountpoint.dirlen = sizeof (defaultdir) - 1;
70
71       return;
72     }
73
74   /* OK, do it the hard way.  Look through the /proc/mounts file and if
75      this does not exist through /etc/fstab to find the mount point.  */
76   fp = __setmntent ("/proc/mounts", "r");
77   if (__builtin_expect (fp == NULL, 0))
78     {
79       fp = __setmntent (_PATH_MNTTAB, "r");
80       if (__builtin_expect (fp == NULL, 0))
81         /* There is nothing we can do.  Blind guesses are not helpful.  */
82         return;
83     }
84
85   /* Now read the entries.  */
86   while ((mp = __getmntent_r (fp, &resmem, buf, sizeof buf)) != NULL)
87     /* The original name is "shm" but this got changed in early Linux
88        2.4.x to "tmpfs".  */
89     if (strcmp (mp->mnt_type, "tmpfs") == 0)
90       {
91         /* Found it.  There might be more than one place where the
92            filesystem is mounted but one is enough for us.  */
93         size_t namelen;
94
95         /* First make sure this really is the correct entry.  At least
96            some versions of the kernel give wrong information because
97            of the implicit mount of the shmfs for SysV IPC.  */
98         if (__statfs (mp->mnt_dir, &f) != 0 || f.f_type != SHMFS_SUPER_MAGIC)
99           continue;
100
101         namelen = strlen (mp->mnt_dir);
102
103         if (namelen == 0)
104           /* Hum, maybe some crippled entry.  Keep on searching.  */
105           continue;
106
107         mountpoint.dir = (char *) malloc (namelen + 2);
108         if (mountpoint.dir != NULL)
109           {
110             char *cp = __mempcpy (mountpoint.dir, mp->mnt_dir, namelen);
111             if (cp[-1] != '/')
112               *cp++ = '/';
113             *cp = '\0';
114             mountpoint.dirlen = cp - mountpoint.dir;
115           }
116
117         break;
118       }
119
120   /* Close the stream.  */
121   __endmntent (fp);
122 }
123
124
125 /* Open shared memory object.  This implementation assumes the shmfs
126    implementation introduced in the late 2.3.x kernel series to be
127    available.  Normally the filesystem will be mounted at /dev/shm but
128    we fall back on searching for the actual mount point should opening
129    such a file fail.  */
130 int
131 shm_open (const char *name, int oflag, mode_t mode)
132 {
133   size_t namelen;
134   char *fname;
135   int fd;
136
137   /* Determine where the shmfs is mounted.  */
138   __libc_once (once, where_is_shmfs);
139
140   /* If we don't know the mount points there is nothing we can do.  Ever.  */
141   if (mountpoint.dir == NULL)
142     {
143       __set_errno (ENOSYS);
144       return -1;
145     }
146
147   /* Construct the filename.  */
148   while (name[0] == '/')
149     ++name;
150
151   namelen = strlen (name);
152
153   /* Validate the filename.  */
154   if (name[0] == '\0' || namelen > NAME_MAX || strchr (name, '/') != NULL)
155     {
156       __set_errno (EINVAL);
157       return -1;
158     }
159
160   fname = (char *) alloca (mountpoint.dirlen + namelen + 1);
161   __mempcpy (__mempcpy (fname, mountpoint.dir, mountpoint.dirlen),
162              name, namelen + 1);
163
164 #ifdef O_CLOEXEC
165   oflag |= O_CLOEXEC;
166 #endif
167
168   /* And get the file descriptor.
169      XXX Maybe we should test each descriptor whether it really is for a
170      file on the shmfs.  If this is what should be done the whole function
171      should be revamped since we can determine whether shmfs is available
172      while trying to open the file, all in one turn.  */
173   fd = open (fname, oflag | O_NOFOLLOW, mode);
174   if (fd != -1)
175     {
176 #if !defined O_CLOEXEC || !defined __ASSUME_O_CLOEXEC
177 # ifdef O_CLOEXEC
178       if (have_o_cloexec <= 0)
179 # endif
180         {
181           /* We got a descriptor.  Now set the FD_CLOEXEC bit.  */
182           int flags = fcntl (fd, F_GETFD, 0);
183
184           if (__builtin_expect (flags, 0) >= 0)
185             {
186 # ifdef O_CLOEXEC
187               if (have_o_cloexec == 0)
188                 have_o_cloexec = (flags & FD_CLOEXEC) == 0 ? -1 : 1;
189               if (have_o_cloexec < 0)
190 # endif
191                 {
192                   flags |= FD_CLOEXEC;
193                   flags = fcntl (fd, F_SETFD, flags);
194                 }
195             }
196
197           if (flags == -1)
198             {
199               /* Something went wrong.  We cannot return the descriptor.  */
200               int save_errno = errno;
201               close (fd);
202               fd = -1;
203               __set_errno (save_errno);
204             }
205         }
206 #endif
207     }
208   else if (__builtin_expect (errno == EISDIR, 0))
209     /* It might be better to fold this error with EINVAL since
210        directory names are just another example for unsuitable shared
211        object names and the standard does not mention EISDIR.  */
212     __set_errno (EINVAL);
213
214   return fd;
215 }
216
217
218 /* Unlink a shared memory object.  */
219 int
220 shm_unlink (const char *name)
221 {
222   size_t namelen;
223   char *fname;
224
225   /* Determine where the shmfs is mounted.  */
226   __libc_once (once, where_is_shmfs);
227
228   if (mountpoint.dir == NULL)
229     {
230       /* We cannot find the shmfs.  If `name' is really a shared
231          memory object it must have been created by another process
232          and we have no idea where that process found the mountpoint.  */
233       __set_errno (ENOENT);
234       return -1;
235     }
236
237   /* Construct the filename.  */
238   while (name[0] == '/')
239     ++name;
240
241   namelen = strlen (name);
242
243   /* Validate the filename.  */
244   if (name[0] == '\0' || namelen > NAME_MAX || strchr (name, '/') != NULL)
245     {
246       __set_errno (ENOENT);
247       return -1;
248     }
249
250   fname = (char *) alloca (mountpoint.dirlen + namelen + 1);
251   __mempcpy (__mempcpy (fname, mountpoint.dir, mountpoint.dirlen),
252              name, namelen + 1);
253
254   /* And remove the file.  */
255   int ret = unlink (fname);
256   if (ret < 0 && errno == EPERM)
257     __set_errno (EACCES);
258   return ret;
259 }
260
261
262 /* Make sure the table is freed if we want to free everything before
263    exiting.  */
264 libc_freeres_fn (freeit)
265 {
266   if (mountpoint.dir != defaultdir)
267     free (mountpoint.dir);
268 }