Compare commits

..

1 Commits

Author SHA1 Message Date
TapTap f3dd2e326e fix: memory/null safety bugs — issues #74, #72, #69, #64, #60, #50, #49, #65
CI / lint (pull_request) Failing after 7s
CI / build-and-test (pull_request) Has been skipped
CI / sanitizers (address) (pull_request) Has been skipped
CI / sanitizers (undefined) (pull_request) Has been skipped
CI / fuzz-build (pull_request) Has been skipped
CI / coverage (pull_request) Has been skipped
CI / valgrind (pull_request) Has been skipped
2026-07-20 18:44:10 +02:00
11 changed files with 23 additions and 37 deletions
+1
Submodule _deps/xxhash-src added at e626a72bc2
+1 -1
View File
@@ -159,7 +159,7 @@ Chunk* directory_scanner_next(DirectoryScanner* scanner) {
char* cur_path = path_cat(scanner->current_path, entry->d_name);
struct stat stats;
if (lstat(cur_path, &stats) != 0) {
if (stat(cur_path, &stats) != 0) {
free(cur_path);
continue;
}
+1 -1
View File
@@ -1,6 +1,6 @@
#include "data.h"
#include "log.h"
#include <stdlib.h>
#include "stdlib.h"
Data* data_create_empty(size_t data_size) {
/* malloc(0) is UB; allocate at least 1 byte but preserve requested size */
+2 -8
View File
@@ -178,20 +178,14 @@ bool send_data(int file_descriptor, const Data* data) {
return false;
if (!send_n_data(file_descriptor, data->data, data_size))
return false;
log_message(LOG_LEVEL_DEBUG, "Send %llu data", data_size);
log_message(LOG_LEVEL_DEBUG, "Send %lld data", data_size);
return true;
}
#define MAX_DATA_SIZE (1024ULL * 1024 * 1024)
Data* receive_data(int file_descriptor) {
unsigned long long size = 0;
if (!receive_n_data(file_descriptor, &size, sizeof(unsigned long long)))
return NULL;
if ((size_t)size != size || size > MAX_DATA_SIZE) {
log_message(LOG_LEVEL_ERROR, "receive_data size %llu exceeds limits", size);
return NULL;
}
void* data = malloc((size_t)size);
if (data == NULL)
return NULL;
@@ -199,7 +193,7 @@ Data* receive_data(int file_descriptor) {
free(data);
return NULL;
}
log_message(LOG_LEVEL_DEBUG, "Received %llu data", size);
log_message(LOG_LEVEL_DEBUG, "Received %lld data", size);
return data_create(data, (size_t)size);
}
+3 -3
View File
@@ -5,9 +5,6 @@
#include <stdbool.h>
#include <stddef.h>
/* Maximum allowed string size for receive_str (10 MB) */
#define MAX_STRING_SIZE (10 * 1024 * 1024)
typedef struct ssl_st SSL;
typedef int Status;
@@ -39,3 +36,6 @@ bool send_status(int file_descriptor, Status status);
bool receive_status(int file_descriptor, Status* status);
#endif
/* Maximum allowed string size for receive_str (10 MB) */
#define MAX_STRING_SIZE (10 * 1024 * 1024)
+7 -2
View File
@@ -138,19 +138,24 @@ Client* client_connect_ssh(const char* destination, int port) {
ssh_argv[ac++] = "-o";
ssh_argv[ac++] = "ControlPath=~/.cache/fastsync-%r@%h:%p";
if (port > 0 && port != 22) {
if ((size_t)ac + 2 >= ssh_argv_max)
if ((size_t)ac + 2 >= ssh_argv_max) {
free(ssh_argv);
_exit(1);
}
ssh_argv[ac++] = "-p";
snprintf(port_str, sizeof(port_str), "%d", port);
ssh_argv[ac++] = port_str;
}
if ((size_t)ac + 3 >= ssh_argv_max)
if ((size_t)ac + 3 >= ssh_argv_max) {
free(ssh_argv);
_exit(1);
}
ssh_argv[ac++] = ssh_user;
ssh_argv[ac++] = "fastsync-server";
ssh_argv[ac++] = "--stdio";
ssh_argv[ac] = NULL;
execvp("ssh", ssh_argv);
free(ssh_argv);
perror("exec of ssh failed");
ssize_t wret = write(exec_pipe[1], "x", 1);
(void)wret;
+5 -4
View File
@@ -108,7 +108,7 @@ static void delete_extras_walk(const char* abs_path, const char* rel_path, Array
if (!dir)
return;
bool all_removed = true;
const struct dirent* entry;
struct dirent* entry;
while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
@@ -158,17 +158,18 @@ void delete_extras(const char* dest_root, ArrayList* manifest) {
delete_extras_walk(dest_root, "", manifest);
}
char* path_cat(const char* path1, const char* path2) {
char* path_cat(const char* path1, char* path2) {
if (path1 == NULL || *path1 == '\0')
return str_dup(path2);
if (path2 == NULL || *path2 == '\0')
return str_dup(path1);
int path1_len = strlen(path1);
int path2_len = strlen(path2);
char* path2_pointer = path2;
if (path1[path1_len - 1] == '/')
path1_len -= 1;
if (path2[0] == '/') {
path2++;
path2_pointer += 1;
path2_len -= 1;
}
char* new_path = malloc(path1_len + path2_len + 2);
@@ -176,7 +177,7 @@ char* path_cat(const char* path1, const char* path2) {
return NULL;
memcpy(new_path, path1, path1_len);
new_path[path1_len] = '/';
memcpy(new_path + path1_len + 1, path2, path2_len);
memcpy(new_path + path1_len + 1, path2_pointer, path2_len);
new_path[path1_len + path2_len + 1] = '\0';
return new_path;
}
+1 -1
View File
@@ -6,7 +6,7 @@
bool mkdir_r(const char* path);
char* str_dup(const char* string);
char* path_cat(const char* path1, const char* path2);
char* path_cat(const char* path1, char* path2);
bool glob_match(const char* pattern, const char* str);
void delete_extras(const char* dest_root, ArrayList* manifest);
+1 -1
View File
@@ -271,7 +271,7 @@ void test_file() {
test_to_disk_basic();
test_to_disk_creates_dirs();
test_file_content_to_buffer();
if (!is_running_under_valgrind()) {
if (!getenv("FASTSYNC_UNDER_VALGRIND")) {
// Fork tests are skipped under valgrind because the parent process runs
// orders of magnitude slower than the child (parent is instrumented, child
// is not), which causes pipe-based protocol handshake timeouts. The parent
+1 -1
View File
@@ -179,7 +179,7 @@ static void test_receive_str_oversized() {
size_t huge = MAX_STRING_SIZE + 1;
EXPECT_TRUE(send_n_data(0, &huge, sizeof(size_t)));
const char* received = receive_str(0);
char* received = receive_str(0);
EXPECT_NULL(received);
close(p[0]);
-15
View File
@@ -2,24 +2,9 @@
#define TEST_UTILS_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
// Detect if running under valgrind by checking /proc/self/maps for vgpreload.
// This is used to skip fork-based tests that are incompatible with valgrind
// (the instrumented parent runs too slowly, causing pipe timeouts).
static inline bool is_running_under_valgrind(void) {
FILE* f = fopen("/proc/self/maps", "r");
if (!f)
return false;
char buf[4096];
size_t n = fread(buf, 1, sizeof(buf) - 1, f);
fclose(f);
buf[n] = '\0';
return strstr(buf, "vgpreload") != NULL;
}
// Global test suite status
extern int tests_run;
extern int tests_failed;