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'.
22static 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).
31static 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
40bool 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
78bool 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
114bool 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
146RUN_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