Skip to content

Commit 81564fe

Browse files
committed
fix(vfs): Fix TOCTOU vulnerability
1 parent 2d0b267 commit 81564fe

File tree

1 file changed

+78
-91
lines changed

1 file changed

+78
-91
lines changed

libraries/FS/src/vfs_api.cpp

Lines changed: 78 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
// limitations under the License.
1414

1515
#include "vfs_api.h"
16+
#include <errno.h>
17+
#include <stdlib.h>
18+
#include <string.h>
1619

1720
using namespace fs;
1821

@@ -38,8 +41,47 @@ FileImplPtr VFSImpl::open(const char *fpath, const char *mode, const bool create
3841
strcpy(temp, _mountpoint);
3942
strcat(temp, fpath);
4043

44+
// Try to open as file first - let the file operation handle errors
45+
if (mode && mode[0] != 'r') {
46+
// For write modes, attempt to create directories if needed
47+
if (create) {
48+
char *token;
49+
char *folder = (char *)malloc(strlen(fpath) + 1);
50+
51+
int start_index = 0;
52+
int end_index = 0;
53+
54+
token = strchr(fpath + 1, '/');
55+
end_index = (token - fpath);
56+
57+
while (token != NULL) {
58+
memcpy(folder, fpath + start_index, end_index - start_index);
59+
folder[end_index - start_index] = '\0';
60+
61+
if (!VFSImpl::mkdir(folder)) {
62+
log_e("Creating folder: %s failed!", folder);
63+
free(folder);
64+
free(temp);
65+
return FileImplPtr();
66+
}
67+
68+
token = strchr(token + 1, '/');
69+
if (token != NULL) {
70+
end_index = (token - fpath);
71+
memset(folder, 0, strlen(folder));
72+
}
73+
}
74+
75+
free(folder);
76+
}
77+
78+
// Try to open the file directly - let fopen handle errors
79+
free(temp);
80+
return std::make_shared<VFSFileImpl>(this, fpath, mode);
81+
}
82+
83+
// For read mode, try to open as file first
4184
struct stat st;
42-
//file found
4385
if (!stat(temp, &st)) {
4486
free(temp);
4587
if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)) {
@@ -49,54 +91,15 @@ FileImplPtr VFSImpl::open(const char *fpath, const char *mode, const bool create
4991
return FileImplPtr();
5092
}
5193

52-
//try to open this as directory (might be mount point)
94+
// Try to open as directory (might be mount point)
5395
DIR *d = opendir(temp);
5496
if (d) {
5597
closedir(d);
5698
free(temp);
5799
return std::make_shared<VFSFileImpl>(this, fpath, mode);
58100
}
59101

60-
//file not found but mode permits file creation without folder creation
61-
if ((mode && mode[0] != 'r') && (!create)) {
62-
free(temp);
63-
return std::make_shared<VFSFileImpl>(this, fpath, mode);
64-
}
65-
66-
////file not found but mode permits file creation and folder creation
67-
if ((mode && mode[0] != 'r') && create) {
68-
69-
char *token;
70-
char *folder = (char *)malloc(strlen(fpath) + 1);
71-
72-
int start_index = 0;
73-
int end_index = 0;
74-
75-
token = strchr(fpath + 1, '/');
76-
end_index = (token - fpath);
77-
78-
while (token != NULL) {
79-
memcpy(folder, fpath + start_index, end_index - start_index);
80-
folder[end_index - start_index] = '\0';
81-
82-
if (!VFSImpl::mkdir(folder)) {
83-
log_e("Creating folder: %s failed!", folder);
84-
return FileImplPtr();
85-
}
86-
87-
token = strchr(token + 1, '/');
88-
if (token != NULL) {
89-
end_index = (token - fpath);
90-
memset(folder, 0, strlen(folder));
91-
}
92-
}
93-
94-
free(folder);
95-
free(temp);
96-
return std::make_shared<VFSFileImpl>(this, fpath, mode);
97-
}
98-
99-
log_e("%s does not exist, no permits for creation", temp);
102+
// File not found and read mode - return empty
100103
free(temp);
101104
return FileImplPtr();
102105
}
@@ -125,10 +128,7 @@ bool VFSImpl::rename(const char *pathFrom, const char *pathTo) {
125128
log_e("bad arguments");
126129
return false;
127130
}
128-
if (!exists(pathFrom)) {
129-
log_e("%s does not exists", pathFrom);
130-
return false;
131-
}
131+
132132
size_t mountpointLen = strlen(_mountpoint);
133133
char *temp1 = (char *)malloc(strlen(pathFrom) + mountpointLen + 1);
134134
if (!temp1) {
@@ -148,6 +148,7 @@ bool VFSImpl::rename(const char *pathFrom, const char *pathTo) {
148148
strcpy(temp2, _mountpoint);
149149
strcat(temp2, pathTo);
150150

151+
// Let rename() handle the error if source doesn't exist
151152
auto rc = ::rename(temp1, temp2);
152153
free(temp1);
153154
free(temp2);
@@ -165,16 +166,6 @@ bool VFSImpl::remove(const char *fpath) {
165166
return false;
166167
}
167168

168-
VFSFileImpl f(this, fpath, "r");
169-
if (!f || f.isDirectory()) {
170-
if (f) {
171-
f.close();
172-
}
173-
log_e("%s does not exists or is directory", fpath);
174-
return false;
175-
}
176-
f.close();
177-
178169
char *temp = (char *)malloc(strlen(fpath) + strlen(_mountpoint) + 1);
179170
if (!temp) {
180171
log_e("malloc failed");
@@ -184,6 +175,7 @@ bool VFSImpl::remove(const char *fpath) {
184175
strcpy(temp, _mountpoint);
185176
strcat(temp, fpath);
186177

178+
// Let unlink() handle the error if file doesn't exist
187179
auto rc = unlink(temp);
188180
free(temp);
189181
return rc == 0;
@@ -195,17 +187,6 @@ bool VFSImpl::mkdir(const char *fpath) {
195187
return false;
196188
}
197189

198-
VFSFileImpl f(this, fpath, "r");
199-
if (f && f.isDirectory()) {
200-
f.close();
201-
//log_w("%s already exists", fpath);
202-
return true;
203-
} else if (f) {
204-
f.close();
205-
log_e("%s is a file", fpath);
206-
return false;
207-
}
208-
209190
char *temp = (char *)malloc(strlen(fpath) + strlen(_mountpoint) + 1);
210191
if (!temp) {
211192
log_e("malloc failed");
@@ -215,9 +196,15 @@ bool VFSImpl::mkdir(const char *fpath) {
215196
strcpy(temp, _mountpoint);
216197
strcat(temp, fpath);
217198

199+
// Let mkdir() handle the error if directory already exists
218200
auto rc = ::mkdir(temp, ACCESSPERMS);
219201
free(temp);
220-
return rc == 0;
202+
203+
// EEXIST means directory already exists, which is fine
204+
if (rc == 0 || errno == EEXIST) {
205+
return true;
206+
}
207+
return false;
221208
}
222209

223210
bool VFSImpl::rmdir(const char *fpath) {
@@ -231,16 +218,6 @@ bool VFSImpl::rmdir(const char *fpath) {
231218
return false;
232219
}
233220

234-
VFSFileImpl f(this, fpath, "r");
235-
if (!f || !f.isDirectory()) {
236-
if (f) {
237-
f.close();
238-
}
239-
log_e("%s does not exists or is a file", fpath);
240-
return false;
241-
}
242-
f.close();
243-
244221
char *temp = (char *)malloc(strlen(fpath) + strlen(_mountpoint) + 1);
245222
if (!temp) {
246223
log_e("malloc failed");
@@ -250,6 +227,7 @@ bool VFSImpl::rmdir(const char *fpath) {
250227
strcpy(temp, _mountpoint);
251228
strcat(temp, fpath);
252229

230+
// Let rmdir() handle the error if directory doesn't exist
253231
auto rc = ::rmdir(temp);
254232
free(temp);
255233
return rc == 0;
@@ -271,8 +249,27 @@ VFSFileImpl::VFSFileImpl(VFSImpl *fs, const char *fpath, const char *mode) : _fs
271249
return;
272250
}
273251

252+
// Try to open as file first
253+
_f = fopen(temp, mode);
254+
if (_f) {
255+
_isDirectory = false;
256+
if (_stat.st_blksize == 0) {
257+
setvbuf(_f, NULL, _IOFBF, DEFAULT_FILE_BUFFER_SIZE);
258+
}
259+
free(temp);
260+
return;
261+
}
262+
263+
// Try to open as directory
264+
_d = opendir(temp);
265+
if (_d) {
266+
_isDirectory = true;
267+
free(temp);
268+
return;
269+
}
270+
271+
// If neither worked, try to stat to determine type
274272
if (!stat(temp, &_stat)) {
275-
//file found
276273
if (S_ISREG(_stat.st_mode)) {
277274
_isDirectory = false;
278275
_f = fopen(temp, mode);
@@ -292,18 +289,8 @@ VFSFileImpl::VFSFileImpl(VFSImpl *fs, const char *fpath, const char *mode) : _fs
292289
log_e("Unknown type 0x%08X for file %s", ((_stat.st_mode) & _IFMT), temp);
293290
}
294291
} else {
295-
//file not found
296-
if (!mode || mode[0] == 'r') {
297-
//try to open as directory
298-
_d = opendir(temp);
299-
if (_d) {
300-
_isDirectory = true;
301-
} else {
302-
_isDirectory = false;
303-
//log_w("stat(%s) failed", temp);
304-
}
305-
} else {
306-
//lets create this new file
292+
// File not found - this is normal for write modes
293+
if (mode && mode[0] != 'r') {
307294
_isDirectory = false;
308295
_f = fopen(temp, mode);
309296
if (!_f) {

0 commit comments

Comments
 (0)