Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/tools/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,14 @@ static int file_read(const char *path, char *result_buf, size_t max_len)
return -1;
}
size_t to_read = (size_t)sz;
if (to_read >= max_len - 1) to_read = max_len - 1;
/* A prefix with return 0 is the whole file to the model. write_file would
* then replace the original and drop the unread tail. */
if (to_read + 1 > max_len) {
fclose(f);
snprintf(result_buf, max_len,
"{\"error\":\"file exceeds result buffer\",\"bytes\":%ld}", sz);
return -1;
}
size_t n = fread(result_buf, 1, to_read, f);
fclose(f);
result_buf[n] = '\0';
Expand Down
43 changes: 43 additions & 0 deletions tests/test_file.c
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,48 @@ static void test_write_in_workspace_symlink_alias_rejected(void)
rmdir(tmpdir);
}

static void test_read_rejects_file_larger_than_result_buffer(void)
{
char tmpdir[64];
char notes_path[PATH_MAX];
char config_path[PATH_MAX];
char args[PATH_MAX + 64];
char buf[32];
char exact[8];
FILE *f;
config_t *cfg;
const tool_t *t;
int i;

snprintf(tmpdir, sizeof(tmpdir), "/tmp/sc_test_readcap_%d", (int)getpid());
if (mkdir(tmpdir, 0755) != 0 && errno != EEXIST)
return;
snprintf(notes_path, sizeof(notes_path), "%s/notes.md", tmpdir);
f = fopen(notes_path, "w");
MU_ASSERT(f != NULL, "create oversized notes.md");
for (i = 0; i < 64; i++)
fputc('A', f);
fclose(f);
cfg = make_ws_config(tmpdir, config_path, sizeof(config_path));
MU_ASSERT(cfg != NULL, "readcap: load config");
tool_file_set_config(cfg);
t = tool_file_get();
snprintf(args, sizeof(args), "{\"operation\":\"read_file\",\"path\":\"%s\"}", notes_path);
MU_ASSERT(t->execute(args, buf, sizeof(buf)) == -1, "oversize read fails");
MU_ASSERT(strstr(buf, "error") != NULL, "oversize read reports an error");
MU_ASSERT(strstr(buf, "AAAAAAAAAAAAAAAA") == NULL, "oversize read is not a raw prefix");
f = fopen(notes_path, "w");
MU_ASSERT(f != NULL, "rewrite exact-fit notes.md");
fputs("0123456", f);
fclose(f);
MU_ASSERT(t->execute(args, exact, sizeof(exact)) == 0, "exact-fit read succeeds");
MU_ASSERT(strcmp(exact, "0123456") == 0, "exact-fit read returns the whole file");
config_free(cfg);
unlink(config_path);
unlink(notes_path);
rmdir(tmpdir);
}

int main(void)
{
MU_RUN(test_file_read_write_list);
Expand All @@ -577,6 +619,7 @@ int main(void)
MU_RUN(test_file_write_failure_preserves_existing);
MU_RUN(test_write_does_not_clobber_sibling_tmp);
MU_RUN(test_write_in_workspace_symlink_alias_rejected);
MU_RUN(test_read_rejects_file_larger_than_result_buffer);
printf("%d tests run, %d failed\n", tests_run, tests_failed);
return tests_failed ? 1 : 0;
}
Loading