1 // Copyright 2017 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <assert.h>
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <limits.h>
9 #include <stdbool.h>
10 #include <stdint.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sys/stat.h>
15 #include <unistd.h>
16 
17 #include "filesystems.h"
18 #include "misc.h"
19 
20 // Given a buffer of size PATH_MAX, make a 'len' byte long filename (not including null) consisting
21 // of the character 'c'.
make_name(char * buf,size_t len,char c)22 static void make_name(char* buf, size_t len, char c) {
23     memset(buf, ':', 2);
24     buf += 2;
25     memset(buf, c, len);
26     buf[len] = '\0';
27 }
28 
29 // Extends 'name' with a string 'len' bytes long, of the character 'c'.
30 // Assumes 'name' is large enough to hold 'len' additional bytes (and a new null character).
extend_name(char * name,size_t len,char c)31 static void extend_name(char* name, size_t len, char c) {
32     char buf[PATH_MAX];
33     assert(len < PATH_MAX);
34     memset(buf, c, len);
35     buf[len] = '\0';
36     strcat(name, "/");
37     strcat(name, buf);
38 }
39 
test_overflow_name(void)40 bool test_overflow_name(void) {
41     BEGIN_TEST;
42 
43     char name_largest[PATH_MAX];
44     char name_largest_alt[PATH_MAX];
45     char name_too_large[PATH_MAX];
46     make_name(name_largest, NAME_MAX, 'a');
47     make_name(name_largest_alt, NAME_MAX, 'b');
48     make_name(name_too_large, NAME_MAX + 1, 'a');
49 
50     // Try opening, closing, renaming, and unlinking the largest acceptable name
51     int fd = open(name_largest, O_RDWR | O_CREAT | O_EXCL, 0644);
52     ASSERT_GT(fd, 0, "");
53     ASSERT_EQ(close(fd), 0, "");
54     ASSERT_EQ(rename(name_largest, name_largest_alt), 0, "");
55     ASSERT_EQ(rename(name_largest_alt, name_largest), 0, "");
56 
57     ASSERT_EQ(rename(name_largest, name_too_large), -1, "");
58     ASSERT_EQ(rename(name_too_large, name_largest), -1, "");
59     ASSERT_EQ(unlink(name_largest), 0, "");
60 
61     // Try it with a directory too
62     ASSERT_EQ(mkdir(name_largest, 0755), 0, "");
63     ASSERT_EQ(rename(name_largest, name_largest_alt), 0, "");
64     ASSERT_EQ(rename(name_largest_alt, name_largest), 0, "");
65 
66     ASSERT_EQ(rename(name_largest, name_too_large), -1, "");
67     ASSERT_EQ(rename(name_too_large, name_largest), -1, "");
68     ASSERT_EQ(unlink(name_largest), 0, "");
69 
70     // Try opening an unacceptably large name
71     ASSERT_EQ(open(name_too_large, O_RDWR | O_CREAT | O_EXCL, 0644), -1, "");
72     // Try it with a directory too
73     ASSERT_EQ(mkdir(name_too_large, 0755), -1, "");
74 
75     END_TEST;
76 }
77 
test_overflow_path(void)78 bool test_overflow_path(void) {
79     BEGIN_TEST;
80 
81     // Make the name buffer larger than PATH_MAX so we don't overflow
82     char name[2 * PATH_MAX];
83 
84     int depth = 0;
85 
86     // Create an initial directory
87     make_name(name, NAME_MAX, 'a');
88     ASSERT_EQ(mkdir(name, 0755), 0, "");
89     depth++;
90     // Create child directories until we hit PATH_MAX
91     while (true) {
92         extend_name(name, NAME_MAX, 'a');
93         int r = mkdir(name, 0755);
94         if (r < 0) {
95             assert(errno == ENAMETOOLONG);
96             break;
97         }
98         depth++;
99     }
100 
101     // Remove all child directories
102     while (depth != 0) {
103         char* last_slash = strrchr(name, '/');
104         assert(last_slash != NULL);
105         assert(*last_slash == '/');
106         *last_slash = '\0';
107         ASSERT_EQ(unlink(name), 0, "");
108         depth--;
109     }
110 
111     END_TEST;
112 }
113 
test_overflow_integer(void)114 bool test_overflow_integer(void) {
115     BEGIN_TEST;
116 
117     int fd = open("::file", O_CREAT | O_RDWR | O_EXCL, 0644);
118     ASSERT_GT(fd, 0, "");
119 
120     // TODO(smklein): Test extremely large reads/writes when remoteio can handle them without
121     // crashing
122     /*
123     char buf[4096];
124     ASSERT_EQ(write(fd, buf, SIZE_MAX - 1), -1, "");
125     ASSERT_EQ(write(fd, buf, SIZE_MAX), -1, "");
126 
127     ASSERT_EQ(read(fd, buf, SIZE_MAX - 1), -1, "");
128     ASSERT_EQ(read(fd, buf, SIZE_MAX), -1, "");
129     */
130 
131     ASSERT_EQ(ftruncate(fd, INT_MIN), -1, "");
132     ASSERT_EQ(ftruncate(fd, -1), -1, "");
133     ASSERT_EQ(ftruncate(fd, SIZE_MAX - 1), -1, "");
134     ASSERT_EQ(ftruncate(fd, SIZE_MAX), -1, "");
135 
136     ASSERT_EQ(lseek(fd, INT_MIN, SEEK_SET), -1, "");
137     ASSERT_EQ(lseek(fd, -1, SEEK_SET), -1, "");
138     ASSERT_EQ(lseek(fd, SIZE_MAX - 1, SEEK_SET), -1, "");
139     ASSERT_EQ(lseek(fd, SIZE_MAX, SEEK_SET), -1, "");
140     ASSERT_EQ(close(fd), 0, "");
141     ASSERT_EQ(unlink("::file"), 0, "");
142 
143     END_TEST;
144 }
145 
146 RUN_FOR_ALL_FILESYSTEMS(overflow_tests,
147     RUN_TEST_MEDIUM(test_overflow_name)
148     RUN_TEST_MEDIUM(test_overflow_path)
149     RUN_TEST_MEDIUM(test_overflow_integer)
150 )
151