chiark / gitweb /
networkd: fix use-after-free
[elogind.git] / src / shared / memfd.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2013 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <stdio.h>
23 #include <fcntl.h>
24 #include <sys/ioctl.h>
25 #include <sys/mman.h>
26 #include <sys/prctl.h>
27
28 #include "util.h"
29 #include "bus-label.h"
30 #include "missing.h"
31 #include "memfd.h"
32
33 #include "sd-bus.h"
34
35 int memfd_new(int *fd, const char *name) {
36
37         _cleanup_free_ char *g = NULL;
38         int n;
39
40         assert_return(fd, -EINVAL);
41
42         if (name) {
43                 /* The kernel side is pretty picky about the character
44                  * set here, let's do the usual bus escaping to deal
45                  * with that. */
46
47                 g = bus_label_escape(name);
48                 if (!g)
49                         return -ENOMEM;
50
51                 name = g;
52
53         } else {
54                 char pr[17] = {};
55
56                 /* If no name is specified we generate one. We include
57                  * a hint indicating our library implementation, and
58                  * add the thread name to it */
59
60                 assert_se(prctl(PR_GET_NAME, (unsigned long) pr) >= 0);
61
62                 if (isempty(pr))
63                         name = "sd";
64                 else {
65                         _cleanup_free_ char *e = NULL;
66
67                         e = bus_label_escape(pr);
68                         if (!e)
69                                 return -ENOMEM;
70
71                         g = strappend("sd-", e);
72                         if (!g)
73                                 return -ENOMEM;
74
75                         name = g;
76                 }
77         }
78
79         n = memfd_create(name, MFD_ALLOW_SEALING);
80         if (n < 0)
81                 return -errno;
82
83         *fd = n;
84         return 0;
85 }
86
87 int memfd_map(int fd, uint64_t offset, size_t size, void **p) {
88         void *q;
89         int sealed;
90
91         assert_return(fd >= 0, -EINVAL);
92         assert_return(size > 0, -EINVAL);
93         assert_return(p, -EINVAL);
94
95         sealed = memfd_get_sealed(fd);
96         if (sealed < 0)
97                 return sealed;
98
99         if (sealed)
100                 q = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, offset);
101         else
102                 q = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset);
103
104         if (q == MAP_FAILED)
105                 return -errno;
106
107         *p = q;
108         return 0;
109 }
110
111 int memfd_set_sealed(int fd) {
112         int r;
113
114         assert_return(fd >= 0, -EINVAL);
115
116         r = fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE);
117         if (r < 0)
118                 return -errno;
119
120         return 0;
121 }
122
123 int memfd_get_sealed(int fd) {
124         int r;
125
126         assert_return(fd >= 0, -EINVAL);
127
128         r = fcntl(fd, F_GET_SEALS);
129         if (r < 0)
130                 return -errno;
131
132         return (r & (F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE)) ==
133                     (F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE);
134 }
135
136 int memfd_get_size(int fd, uint64_t *sz) {
137         int r;
138         struct stat stat;
139
140         assert_return(fd >= 0, -EINVAL);
141         assert_return(sz, -EINVAL);
142
143         r = fstat(fd, &stat);
144         if (r < 0)
145                 return -errno;
146
147         *sz = stat.st_size;
148         return r;
149 }
150
151 int memfd_set_size(int fd, uint64_t sz) {
152         int r;
153
154         assert_return(fd >= 0, -EINVAL);
155
156         r = ftruncate(fd, sz);
157         if (r < 0)
158                 return -errno;
159
160         return r;
161 }
162
163 int memfd_new_and_map(int *fd, const char *name, size_t sz, void **p) {
164         _cleanup_close_ int n = -1;
165         int r;
166
167         r = memfd_new(&n, name);
168         if (r < 0)
169                 return r;
170
171         r = memfd_set_size(n, sz);
172         if (r < 0)
173                 return r;
174
175         r = memfd_map(n, 0, sz, p);
176         if (r < 0)
177                 return r;
178
179         *fd = n;
180         n = -1;
181         return 0;
182 }
183
184 int memfd_get_name(int fd, char **name) {
185         char path[sizeof("/proc/self/fd/") + DECIMAL_STR_MAX(int)], buf[FILENAME_MAX+1], *e;
186         const char *delim, *end;
187         _cleanup_free_ char *n = NULL;
188         ssize_t k;
189
190         assert_return(fd >= 0, -EINVAL);
191         assert_return(name, -EINVAL);
192
193         sprintf(path, "/proc/self/fd/%i", fd);
194
195         k = readlink(path, buf, sizeof(buf));
196         if (k < 0)
197                 return -errno;
198
199         if ((size_t) k >= sizeof(buf))
200                 return -E2BIG;
201
202         buf[k] = 0;
203
204         delim = strstr(buf, ":[");
205         if (!delim)
206                 return -EIO;
207
208         delim = strchr(delim + 2, ':');
209         if (!delim)
210                 return -EIO;
211
212         delim++;
213
214         end = strchr(delim, ']');
215         if (!end)
216                 return -EIO;
217
218         n = strndup(delim, end - delim);
219         if (!n)
220                 return -ENOMEM;
221
222         e = bus_label_unescape(n);
223         if (!e)
224                 return -ENOMEM;
225
226         *name = e;
227
228         return 0;
229 }