hc
2024-12-19 9370bb92b2d16684ee45cf24e879c93c509162da
kernel/fs/readdir.c
....@@ -20,8 +20,22 @@
2020 #include <linux/syscalls.h>
2121 #include <linux/unistd.h>
2222 #include <linux/compat.h>
23
-
2423 #include <linux/uaccess.h>
24
+
25
+#include <asm/unaligned.h>
26
+
27
+/*
28
+ * Note the "unsafe_put_user() semantics: we goto a
29
+ * label for errors.
30
+ */
31
+#define unsafe_copy_dirent_name(_dst, _src, _len, label) do { \
32
+ char __user *dst = (_dst); \
33
+ const char *src = (_src); \
34
+ size_t len = (_len); \
35
+ unsafe_put_user(0, dst+len, label); \
36
+ unsafe_copy_to_user(dst, src, len, label); \
37
+} while (0)
38
+
2539
2640 int iterate_dir(struct file *file, struct dir_context *ctx)
2741 {
....@@ -88,10 +102,14 @@
88102 * filename length, and the above "soft error" worry means
89103 * that it's probably better left alone until we have that
90104 * issue clarified.
105
+ *
106
+ * Note the PATH_MAX check - it's arbitrary but the real
107
+ * kernel limit on a possible path component, not NAME_MAX,
108
+ * which is the technical standard limit.
91109 */
92110 static int verify_dirent_name(const char *name, int len)
93111 {
94
- if (!len)
112
+ if (len <= 0 || len >= PATH_MAX)
95113 return -EIO;
96114 if (memchr(name, '/', len))
97115 return -EIO;
....@@ -142,17 +160,18 @@
142160 }
143161 buf->result++;
144162 dirent = buf->dirent;
145
- if (!access_ok(VERIFY_WRITE, dirent,
163
+ if (!user_write_access_begin(dirent,
146164 (unsigned long)(dirent->d_name + namlen + 1) -
147165 (unsigned long)dirent))
148166 goto efault;
149
- if ( __put_user(d_ino, &dirent->d_ino) ||
150
- __put_user(offset, &dirent->d_offset) ||
151
- __put_user(namlen, &dirent->d_namlen) ||
152
- __copy_to_user(dirent->d_name, name, namlen) ||
153
- __put_user(0, dirent->d_name + namlen))
154
- goto efault;
167
+ unsafe_put_user(d_ino, &dirent->d_ino, efault_end);
168
+ unsafe_put_user(offset, &dirent->d_offset, efault_end);
169
+ unsafe_put_user(namlen, &dirent->d_namlen, efault_end);
170
+ unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
171
+ user_write_access_end();
155172 return 0;
173
+efault_end:
174
+ user_write_access_end();
156175 efault:
157176 buf->result = -EFAULT;
158177 return -EFAULT;
....@@ -195,7 +214,7 @@
195214 struct getdents_callback {
196215 struct dir_context ctx;
197216 struct linux_dirent __user * current_dir;
198
- struct linux_dirent __user * previous;
217
+ int prev_reclen;
199218 int count;
200219 int error;
201220 };
....@@ -203,12 +222,13 @@
203222 static int filldir(struct dir_context *ctx, const char *name, int namlen,
204223 loff_t offset, u64 ino, unsigned int d_type)
205224 {
206
- struct linux_dirent __user * dirent;
225
+ struct linux_dirent __user *dirent, *prev;
207226 struct getdents_callback *buf =
208227 container_of(ctx, struct getdents_callback, ctx);
209228 unsigned long d_ino;
210229 int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2,
211230 sizeof(long));
231
+ int prev_reclen;
212232
213233 buf->error = verify_dirent_name(name, namlen);
214234 if (unlikely(buf->error))
....@@ -221,29 +241,28 @@
221241 buf->error = -EOVERFLOW;
222242 return -EOVERFLOW;
223243 }
224
- dirent = buf->previous;
225
- if (dirent) {
226
- if (signal_pending(current))
227
- return -EINTR;
228
- if (__put_user(offset, &dirent->d_off))
229
- goto efault;
230
- }
244
+ prev_reclen = buf->prev_reclen;
245
+ if (prev_reclen && signal_pending(current))
246
+ return -EINTR;
231247 dirent = buf->current_dir;
232
- if (__put_user(d_ino, &dirent->d_ino))
248
+ prev = (void __user *) dirent - prev_reclen;
249
+ if (!user_write_access_begin(prev, reclen + prev_reclen))
233250 goto efault;
234
- if (__put_user(reclen, &dirent->d_reclen))
235
- goto efault;
236
- if (copy_to_user(dirent->d_name, name, namlen))
237
- goto efault;
238
- if (__put_user(0, dirent->d_name + namlen))
239
- goto efault;
240
- if (__put_user(d_type, (char __user *) dirent + reclen - 1))
241
- goto efault;
242
- buf->previous = dirent;
243
- dirent = (void __user *)dirent + reclen;
244
- buf->current_dir = dirent;
251
+
252
+ /* This might be 'dirent->d_off', but if so it will get overwritten */
253
+ unsafe_put_user(offset, &prev->d_off, efault_end);
254
+ unsafe_put_user(d_ino, &dirent->d_ino, efault_end);
255
+ unsafe_put_user(reclen, &dirent->d_reclen, efault_end);
256
+ unsafe_put_user(d_type, (char __user *) dirent + reclen - 1, efault_end);
257
+ unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
258
+ user_write_access_end();
259
+
260
+ buf->current_dir = (void __user *)dirent + reclen;
261
+ buf->prev_reclen = reclen;
245262 buf->count -= reclen;
246263 return 0;
264
+efault_end:
265
+ user_write_access_end();
247266 efault:
248267 buf->error = -EFAULT;
249268 return -EFAULT;
....@@ -253,16 +272,12 @@
253272 struct linux_dirent __user *, dirent, unsigned int, count)
254273 {
255274 struct fd f;
256
- struct linux_dirent __user * lastdirent;
257275 struct getdents_callback buf = {
258276 .ctx.actor = filldir,
259277 .count = count,
260278 .current_dir = dirent
261279 };
262280 int error;
263
-
264
- if (!access_ok(VERIFY_WRITE, dirent, count))
265
- return -EFAULT;
266281
267282 f = fdget_pos(fd);
268283 if (!f.file)
....@@ -271,8 +286,10 @@
271286 error = iterate_dir(f.file, &buf.ctx);
272287 if (error >= 0)
273288 error = buf.error;
274
- lastdirent = buf.previous;
275
- if (lastdirent) {
289
+ if (buf.prev_reclen) {
290
+ struct linux_dirent __user * lastdirent;
291
+ lastdirent = (void __user *)buf.current_dir - buf.prev_reclen;
292
+
276293 if (put_user(buf.ctx.pos, &lastdirent->d_off))
277294 error = -EFAULT;
278295 else
....@@ -285,7 +302,7 @@
285302 struct getdents_callback64 {
286303 struct dir_context ctx;
287304 struct linux_dirent64 __user * current_dir;
288
- struct linux_dirent64 __user * previous;
305
+ int prev_reclen;
289306 int count;
290307 int error;
291308 };
....@@ -293,11 +310,12 @@
293310 static int filldir64(struct dir_context *ctx, const char *name, int namlen,
294311 loff_t offset, u64 ino, unsigned int d_type)
295312 {
296
- struct linux_dirent64 __user *dirent;
313
+ struct linux_dirent64 __user *dirent, *prev;
297314 struct getdents_callback64 *buf =
298315 container_of(ctx, struct getdents_callback64, ctx);
299316 int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1,
300317 sizeof(u64));
318
+ int prev_reclen;
301319
302320 buf->error = verify_dirent_name(name, namlen);
303321 if (unlikely(buf->error))
....@@ -305,50 +323,44 @@
305323 buf->error = -EINVAL; /* only used if we fail.. */
306324 if (reclen > buf->count)
307325 return -EINVAL;
308
- dirent = buf->previous;
309
- if (dirent) {
310
- if (signal_pending(current))
311
- return -EINTR;
312
- if (__put_user(offset, &dirent->d_off))
313
- goto efault;
314
- }
326
+ prev_reclen = buf->prev_reclen;
327
+ if (prev_reclen && signal_pending(current))
328
+ return -EINTR;
315329 dirent = buf->current_dir;
316
- if (__put_user(ino, &dirent->d_ino))
330
+ prev = (void __user *)dirent - prev_reclen;
331
+ if (!user_write_access_begin(prev, reclen + prev_reclen))
317332 goto efault;
318
- if (__put_user(0, &dirent->d_off))
319
- goto efault;
320
- if (__put_user(reclen, &dirent->d_reclen))
321
- goto efault;
322
- if (__put_user(d_type, &dirent->d_type))
323
- goto efault;
324
- if (copy_to_user(dirent->d_name, name, namlen))
325
- goto efault;
326
- if (__put_user(0, dirent->d_name + namlen))
327
- goto efault;
328
- buf->previous = dirent;
329
- dirent = (void __user *)dirent + reclen;
330
- buf->current_dir = dirent;
333
+
334
+ /* This might be 'dirent->d_off', but if so it will get overwritten */
335
+ unsafe_put_user(offset, &prev->d_off, efault_end);
336
+ unsafe_put_user(ino, &dirent->d_ino, efault_end);
337
+ unsafe_put_user(reclen, &dirent->d_reclen, efault_end);
338
+ unsafe_put_user(d_type, &dirent->d_type, efault_end);
339
+ unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
340
+ user_write_access_end();
341
+
342
+ buf->prev_reclen = reclen;
343
+ buf->current_dir = (void __user *)dirent + reclen;
331344 buf->count -= reclen;
332345 return 0;
346
+
347
+efault_end:
348
+ user_write_access_end();
333349 efault:
334350 buf->error = -EFAULT;
335351 return -EFAULT;
336352 }
337353
338
-int ksys_getdents64(unsigned int fd, struct linux_dirent64 __user *dirent,
339
- unsigned int count)
354
+SYSCALL_DEFINE3(getdents64, unsigned int, fd,
355
+ struct linux_dirent64 __user *, dirent, unsigned int, count)
340356 {
341357 struct fd f;
342
- struct linux_dirent64 __user * lastdirent;
343358 struct getdents_callback64 buf = {
344359 .ctx.actor = filldir64,
345360 .count = count,
346361 .current_dir = dirent
347362 };
348363 int error;
349
-
350
- if (!access_ok(VERIFY_WRITE, dirent, count))
351
- return -EFAULT;
352364
353365 f = fdget_pos(fd);
354366 if (!f.file)
....@@ -357,23 +369,18 @@
357369 error = iterate_dir(f.file, &buf.ctx);
358370 if (error >= 0)
359371 error = buf.error;
360
- lastdirent = buf.previous;
361
- if (lastdirent) {
372
+ if (buf.prev_reclen) {
373
+ struct linux_dirent64 __user * lastdirent;
362374 typeof(lastdirent->d_off) d_off = buf.ctx.pos;
363
- if (__put_user(d_off, &lastdirent->d_off))
375
+
376
+ lastdirent = (void __user *) buf.current_dir - buf.prev_reclen;
377
+ if (put_user(d_off, &lastdirent->d_off))
364378 error = -EFAULT;
365379 else
366380 error = count - buf.count;
367381 }
368382 fdput_pos(f);
369383 return error;
370
-}
371
-
372
-
373
-SYSCALL_DEFINE3(getdents64, unsigned int, fd,
374
- struct linux_dirent64 __user *, dirent, unsigned int, count)
375
-{
376
- return ksys_getdents64(fd, dirent, count);
377384 }
378385
379386 #ifdef CONFIG_COMPAT
....@@ -411,17 +418,18 @@
411418 }
412419 buf->result++;
413420 dirent = buf->dirent;
414
- if (!access_ok(VERIFY_WRITE, dirent,
421
+ if (!user_write_access_begin(dirent,
415422 (unsigned long)(dirent->d_name + namlen + 1) -
416423 (unsigned long)dirent))
417424 goto efault;
418
- if ( __put_user(d_ino, &dirent->d_ino) ||
419
- __put_user(offset, &dirent->d_offset) ||
420
- __put_user(namlen, &dirent->d_namlen) ||
421
- __copy_to_user(dirent->d_name, name, namlen) ||
422
- __put_user(0, dirent->d_name + namlen))
423
- goto efault;
425
+ unsafe_put_user(d_ino, &dirent->d_ino, efault_end);
426
+ unsafe_put_user(offset, &dirent->d_offset, efault_end);
427
+ unsafe_put_user(namlen, &dirent->d_namlen, efault_end);
428
+ unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
429
+ user_write_access_end();
424430 return 0;
431
+efault_end:
432
+ user_write_access_end();
425433 efault:
426434 buf->result = -EFAULT;
427435 return -EFAULT;
....@@ -458,7 +466,7 @@
458466 struct compat_getdents_callback {
459467 struct dir_context ctx;
460468 struct compat_linux_dirent __user *current_dir;
461
- struct compat_linux_dirent __user *previous;
469
+ int prev_reclen;
462470 int count;
463471 int error;
464472 };
....@@ -466,13 +474,17 @@
466474 static int compat_filldir(struct dir_context *ctx, const char *name, int namlen,
467475 loff_t offset, u64 ino, unsigned int d_type)
468476 {
469
- struct compat_linux_dirent __user * dirent;
477
+ struct compat_linux_dirent __user *dirent, *prev;
470478 struct compat_getdents_callback *buf =
471479 container_of(ctx, struct compat_getdents_callback, ctx);
472480 compat_ulong_t d_ino;
473481 int reclen = ALIGN(offsetof(struct compat_linux_dirent, d_name) +
474482 namlen + 2, sizeof(compat_long_t));
483
+ int prev_reclen;
475484
485
+ buf->error = verify_dirent_name(name, namlen);
486
+ if (unlikely(buf->error))
487
+ return buf->error;
476488 buf->error = -EINVAL; /* only used if we fail.. */
477489 if (reclen > buf->count)
478490 return -EINVAL;
....@@ -481,29 +493,27 @@
481493 buf->error = -EOVERFLOW;
482494 return -EOVERFLOW;
483495 }
484
- dirent = buf->previous;
485
- if (dirent) {
486
- if (signal_pending(current))
487
- return -EINTR;
488
- if (__put_user(offset, &dirent->d_off))
489
- goto efault;
490
- }
496
+ prev_reclen = buf->prev_reclen;
497
+ if (prev_reclen && signal_pending(current))
498
+ return -EINTR;
491499 dirent = buf->current_dir;
492
- if (__put_user(d_ino, &dirent->d_ino))
500
+ prev = (void __user *) dirent - prev_reclen;
501
+ if (!user_write_access_begin(prev, reclen + prev_reclen))
493502 goto efault;
494
- if (__put_user(reclen, &dirent->d_reclen))
495
- goto efault;
496
- if (copy_to_user(dirent->d_name, name, namlen))
497
- goto efault;
498
- if (__put_user(0, dirent->d_name + namlen))
499
- goto efault;
500
- if (__put_user(d_type, (char __user *) dirent + reclen - 1))
501
- goto efault;
502
- buf->previous = dirent;
503
- dirent = (void __user *)dirent + reclen;
504
- buf->current_dir = dirent;
503
+
504
+ unsafe_put_user(offset, &prev->d_off, efault_end);
505
+ unsafe_put_user(d_ino, &dirent->d_ino, efault_end);
506
+ unsafe_put_user(reclen, &dirent->d_reclen, efault_end);
507
+ unsafe_put_user(d_type, (char __user *) dirent + reclen - 1, efault_end);
508
+ unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
509
+ user_write_access_end();
510
+
511
+ buf->prev_reclen = reclen;
512
+ buf->current_dir = (void __user *)dirent + reclen;
505513 buf->count -= reclen;
506514 return 0;
515
+efault_end:
516
+ user_write_access_end();
507517 efault:
508518 buf->error = -EFAULT;
509519 return -EFAULT;
....@@ -513,16 +523,12 @@
513523 struct compat_linux_dirent __user *, dirent, unsigned int, count)
514524 {
515525 struct fd f;
516
- struct compat_linux_dirent __user * lastdirent;
517526 struct compat_getdents_callback buf = {
518527 .ctx.actor = compat_filldir,
519528 .current_dir = dirent,
520529 .count = count
521530 };
522531 int error;
523
-
524
- if (!access_ok(VERIFY_WRITE, dirent, count))
525
- return -EFAULT;
526532
527533 f = fdget_pos(fd);
528534 if (!f.file)
....@@ -531,8 +537,10 @@
531537 error = iterate_dir(f.file, &buf.ctx);
532538 if (error >= 0)
533539 error = buf.error;
534
- lastdirent = buf.previous;
535
- if (lastdirent) {
540
+ if (buf.prev_reclen) {
541
+ struct compat_linux_dirent __user * lastdirent;
542
+ lastdirent = (void __user *)buf.current_dir - buf.prev_reclen;
543
+
536544 if (put_user(buf.ctx.pos, &lastdirent->d_off))
537545 error = -EFAULT;
538546 else