Skip to content

Commit c279a30

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

File tree

1 file changed

+80
-119
lines changed

1 file changed

+80
-119
lines changed

libraries/FS/src/vfs_api.cpp

Lines changed: 80 additions & 119 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,67 +41,49 @@ FileImplPtr VFSImpl::open(const char *fpath, const char *mode, const bool create
3841
strcpy(temp, _mountpoint);
3942
strcat(temp, fpath);
4043

41-
struct stat st;
42-
//file found
43-
if (!stat(temp, &st)) {
44-
free(temp);
45-
if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)) {
46-
return std::make_shared<VFSFileImpl>(this, fpath, mode);
47-
}
48-
log_e("%s has wrong mode 0x%08X", fpath, st.st_mode);
49-
return FileImplPtr();
50-
}
51-
52-
//try to open this as directory (might be mount point)
53-
DIR *d = opendir(temp);
54-
if (d) {
55-
closedir(d);
56-
free(temp);
57-
return std::make_shared<VFSFileImpl>(this, fpath, mode);
58-
}
59-
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();
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+
}
8573
}
8674

87-
token = strchr(token + 1, '/');
88-
if (token != NULL) {
89-
end_index = (token - fpath);
90-
memset(folder, 0, strlen(folder));
91-
}
75+
free(folder);
9276
}
9377

94-
free(folder);
78+
// Try to open the file directly - let fopen handle errors
9579
free(temp);
9680
return std::make_shared<VFSFileImpl>(this, fpath, mode);
9781
}
9882

99-
log_e("%s does not exist, no permits for creation", temp);
83+
// For read mode, let the VFSFileImpl constructor handle the file opening
84+
// This avoids the TOCTOU race condition while maintaining proper functionality
10085
free(temp);
101-
return FileImplPtr();
86+
return std::make_shared<VFSFileImpl>(this, fpath, mode);
10287
}
10388

10489
bool VFSImpl::exists(const char *fpath) {
@@ -125,10 +110,7 @@ bool VFSImpl::rename(const char *pathFrom, const char *pathTo) {
125110
log_e("bad arguments");
126111
return false;
127112
}
128-
if (!exists(pathFrom)) {
129-
log_e("%s does not exists", pathFrom);
130-
return false;
131-
}
113+
132114
size_t mountpointLen = strlen(_mountpoint);
133115
char *temp1 = (char *)malloc(strlen(pathFrom) + mountpointLen + 1);
134116
if (!temp1) {
@@ -148,6 +130,7 @@ bool VFSImpl::rename(const char *pathFrom, const char *pathTo) {
148130
strcpy(temp2, _mountpoint);
149131
strcat(temp2, pathTo);
150132

133+
// Let rename() handle the error if source doesn't exist
151134
auto rc = ::rename(temp1, temp2);
152135
free(temp1);
153136
free(temp2);
@@ -165,16 +148,6 @@ bool VFSImpl::remove(const char *fpath) {
165148
return false;
166149
}
167150

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-
178151
char *temp = (char *)malloc(strlen(fpath) + strlen(_mountpoint) + 1);
179152
if (!temp) {
180153
log_e("malloc failed");
@@ -184,6 +157,7 @@ bool VFSImpl::remove(const char *fpath) {
184157
strcpy(temp, _mountpoint);
185158
strcat(temp, fpath);
186159

160+
// Let unlink() handle the error if file doesn't exist
187161
auto rc = unlink(temp);
188162
free(temp);
189163
return rc == 0;
@@ -195,17 +169,6 @@ bool VFSImpl::mkdir(const char *fpath) {
195169
return false;
196170
}
197171

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-
209172
char *temp = (char *)malloc(strlen(fpath) + strlen(_mountpoint) + 1);
210173
if (!temp) {
211174
log_e("malloc failed");
@@ -215,9 +178,15 @@ bool VFSImpl::mkdir(const char *fpath) {
215178
strcpy(temp, _mountpoint);
216179
strcat(temp, fpath);
217180

181+
// Let mkdir() handle the error if directory already exists
218182
auto rc = ::mkdir(temp, ACCESSPERMS);
219183
free(temp);
220-
return rc == 0;
184+
185+
// EEXIST means directory already exists, which is fine
186+
if (rc == 0 || errno == EEXIST) {
187+
return true;
188+
}
189+
return false;
221190
}
222191

223192
bool VFSImpl::rmdir(const char *fpath) {
@@ -231,16 +200,6 @@ bool VFSImpl::rmdir(const char *fpath) {
231200
return false;
232201
}
233202

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-
244203
char *temp = (char *)malloc(strlen(fpath) + strlen(_mountpoint) + 1);
245204
if (!temp) {
246205
log_e("malloc failed");
@@ -250,6 +209,7 @@ bool VFSImpl::rmdir(const char *fpath) {
250209
strcpy(temp, _mountpoint);
251210
strcat(temp, fpath);
252211

212+
// Let rmdir() handle the error if directory doesn't exist
253213
auto rc = ::rmdir(temp);
254214
free(temp);
255215
return rc == 0;
@@ -271,29 +231,30 @@ VFSFileImpl::VFSFileImpl(VFSImpl *fs, const char *fpath, const char *mode) : _fs
271231
return;
272232
}
273233

274-
if (!stat(temp, &_stat)) {
275-
//file found
276-
if (S_ISREG(_stat.st_mode)) {
277-
_isDirectory = false;
278-
_f = fopen(temp, mode);
279-
if (!_f) {
280-
log_e("fopen(%s) failed", temp);
281-
}
282-
if (_f && (_stat.st_blksize == 0)) {
283-
setvbuf(_f, NULL, _IOFBF, DEFAULT_FILE_BUFFER_SIZE);
284-
}
285-
} else if (S_ISDIR(_stat.st_mode)) {
286-
_isDirectory = true;
287-
_d = opendir(temp);
288-
if (!_d) {
289-
log_e("opendir(%s) failed", temp);
234+
// For read mode, check if file exists first to determine type
235+
if (!mode || mode[0] == 'r') {
236+
if (!stat(temp, &_stat)) {
237+
//file found
238+
if (S_ISREG(_stat.st_mode)) {
239+
_isDirectory = false;
240+
_f = fopen(temp, mode);
241+
if (!_f) {
242+
log_e("fopen(%s) failed", temp);
243+
}
244+
if (_f && (_stat.st_blksize == 0)) {
245+
setvbuf(_f, NULL, _IOFBF, DEFAULT_FILE_BUFFER_SIZE);
246+
}
247+
} else if (S_ISDIR(_stat.st_mode)) {
248+
_isDirectory = true;
249+
_d = opendir(temp);
250+
if (!_d) {
251+
log_e("opendir(%s) failed", temp);
252+
}
253+
} else {
254+
log_e("Unknown type 0x%08X for file %s", ((_stat.st_mode) & _IFMT), temp);
290255
}
291256
} else {
292-
log_e("Unknown type 0x%08X for file %s", ((_stat.st_mode) & _IFMT), temp);
293-
}
294-
} else {
295-
//file not found
296-
if (!mode || mode[0] == 'r') {
257+
//file not found
297258
//try to open as directory
298259
_d = opendir(temp);
299260
if (_d) {
@@ -302,16 +263,16 @@ VFSFileImpl::VFSFileImpl(VFSImpl *fs, const char *fpath, const char *mode) : _fs
302263
_isDirectory = false;
303264
//log_w("stat(%s) failed", temp);
304265
}
305-
} else {
306-
//lets create this new file
307-
_isDirectory = false;
308-
_f = fopen(temp, mode);
309-
if (!_f) {
310-
log_e("fopen(%s) failed", temp);
311-
}
312-
if (_f && (_stat.st_blksize == 0)) {
313-
setvbuf(_f, NULL, _IOFBF, DEFAULT_FILE_BUFFER_SIZE);
314-
}
266+
}
267+
} else {
268+
//lets create this new file
269+
_isDirectory = false;
270+
_f = fopen(temp, mode);
271+
if (!_f) {
272+
log_e("fopen(%s) failed", temp);
273+
}
274+
if (_f && (_stat.st_blksize == 0)) {
275+
setvbuf(_f, NULL, _IOFBF, DEFAULT_FILE_BUFFER_SIZE);
315276
}
316277
}
317278
free(temp);

0 commit comments

Comments
 (0)