From 0197b9b1706c680a449e1db62f0590e0d5bd8f08 Mon Sep 17 00:00:00 2001 From: Adrianno Esnarriaga Sereno Date: Sun, 13 Sep 2026 15:15:49 -0300 Subject: [PATCH 1/3] fix(agent,memory): refuse session JSON truncation that wipes history When serialized session JSON exceeded SESSION_JSON_MAX, append and load silently clipped mid-JSON. The next agent_run parse then treated history as empty. Refuse oversize copies instead of slicing. Refs: #70 --- src/core/agent.c | 20 ++++-- src/core/memory.c | 10 ++- tests/test_agent.c | 170 ++++++++++++++++++++++++++++++++++++++++++++ tests/test_memory.c | 23 ++++++ 4 files changed, 214 insertions(+), 9 deletions(-) diff --git a/src/core/agent.c b/src/core/agent.c index 16e55d5..a019640 100644 --- a/src/core/agent.c +++ b/src/core/agent.c @@ -130,9 +130,13 @@ static int compact_session_via_llm(const char *session_id, char *session_buf, si cJSON_Delete(new_arr); if (!printed) return -1; size_t plen = strlen(printed); - if (plen >= session_buf_size) plen = session_buf_size - 1; - memcpy(session_buf, printed, plen); - session_buf[plen] = '\0'; + /* Refuse mid-JSON truncation: a clipped payload corrupts the session and + * the next agent_run parse treats history as empty (permanent wipe). */ + if (plen >= session_buf_size) { + cJSON_free(printed); + return -1; + } + memcpy(session_buf, printed, plen + 1); cJSON_free(printed); session_save(session_id, session_buf); return 0; @@ -308,9 +312,13 @@ static int append_exchange_to_session_json(const char *existing_json, const char cJSON_Delete(arr); if (!printed) return -1; size_t len = strlen(printed); - if (len >= out_size) len = out_size - 1; - memcpy(out_buf, printed, len); - out_buf[len] = '\0'; + /* Do not truncate: partial JSON saved via session_save is unparseable and + * wipes conversation history on the next agent_run (see agent_prepare_context). */ + if (len >= out_size) { + cJSON_free(printed); + return -1; + } + memcpy(out_buf, printed, len + 1); cJSON_free(printed); return 0; } diff --git a/src/core/memory.c b/src/core/memory.c index b6fc955..1213010 100644 --- a/src/core/memory.c +++ b/src/core/memory.c @@ -245,9 +245,13 @@ int session_load(const char *session_id, char *messages_out, size_t max_len) const char *msg = (const char *)sqlite3_column_text(stmt, 0); if (msg) { size_t n = strlen(msg); - if (n >= max_len) n = max_len - 1; - memcpy(messages_out, msg, n); - messages_out[n] = '\0'; + /* Refuse silent truncation: a clipped messages blob is invalid JSON + * and agent_run would treat the session as empty history. */ + if (n >= max_len) { + sqlite3_finalize(stmt); + return -1; + } + memcpy(messages_out, msg, n + 1); ret = 0; } } diff --git a/tests/test_agent.c b/tests/test_agent.c index 7e0a46f..f8c312d 100644 --- a/tests/test_agent.c +++ b/tests/test_agent.c @@ -8,6 +8,7 @@ #include "core/config.h" #include "core/memory.h" #include "providers/provider.h" +#include "cJSON.h" #include #include #include @@ -718,6 +719,174 @@ static int test_local_offline_note_skipped_for_non_local(void) return 0; } +/* SESSION_JSON_MAX in agent.c is 128 KiB. A reply this large plus a near-full + * prior session forces append_exchange_to_session_json over the cap. */ +#define OVERFLOW_REPLY_BYTES (16 * 1024) + +static int overflow_reply_init(const config_t *cfg) { (void)cfg; return 0; } +static int overflow_reply_chat(const provider_message_t *messages, size_t message_count, + const provider_tool_def_t *tools, size_t tool_count, provider_response_t *response) +{ + char *big; + (void)messages; + (void)message_count; + (void)tools; + (void)tool_count; + response->error = 0; + response->tool_calls = NULL; + response->tool_calls_count = 0; + big = malloc(OVERFLOW_REPLY_BYTES); + if (!big) return -1; + memset(big, 'R', OVERFLOW_REPLY_BYTES - 1); + big[OVERFLOW_REPLY_BYTES - 1] = '\0'; + response->content = big; + return 0; +} +static void overflow_reply_cleanup(void) {} +static const provider_t overflow_reply_provider = { + .name = "overflow_reply", + .init = overflow_reply_init, + .chat = overflow_reply_chat, + .cleanup = overflow_reply_cleanup, +}; + +static int keep_history_init(const config_t *cfg) { (void)cfg; return 0; } +static int keep_history_chat(const provider_message_t *messages, size_t message_count, + const provider_tool_def_t *tools, size_t tool_count, provider_response_t *response) +{ + (void)tools; + (void)tool_count; + response->error = 0; + response->tool_calls = NULL; + response->tool_calls_count = 0; + response->content = strdup("ok"); + spy_roles_clear(); + spy_message_count = message_count; + for (size_t i = 0; i < message_count && i < SPY_SLOTS; i++) { + spy_roles[i] = messages[i].role ? strdup(messages[i].role) : NULL; + if (messages[i].content) { + size_t n = strlen(messages[i].content); + if (n >= SPY_CONTENT_SIZE) n = SPY_CONTENT_SIZE - 1; + memcpy(spy_content[i], messages[i].content, n); + spy_content[i][n] = '\0'; + } else + spy_content[i][0] = '\0'; + } + return 0; +} +static void keep_history_cleanup(void) {} +static const provider_t keep_history_provider = { + .name = "keep_history", + .init = keep_history_init, + .chat = keep_history_chat, + .cleanup = keep_history_cleanup, +}; + +/** + * Concrete trigger: near-cap session JSON + fat assistant reply. + * Before the fix, append truncated mid-JSON, session_save persisted corrupt + * payload, and the next agent_run parse wiped history. After the fix, overflow + * refuses to save and prior history remains parseable. + */ +static int test_session_overflow_does_not_corrupt_history(void) +{ + int failed = 1; + config_t *cfg = NULL; + const char *db_path = "build/test_agent_overflow.db"; + const char *config_path = "build/test_agent_overflow.toml"; + const char *session_id = "cli:overflow"; + const char *marker = "UNIQUE_HISTORY_MARKER_xyz"; + /* Two large messages keep msg_count under max_context so compaction does not shrink first. */ + enum { PAD_A = 62 * 1024, PAD_B = 62 * 1024, LOAD_CAP = 130 * 1024 }; + char *session_json = NULL; + char *pad_a = NULL; + char *pad_b = NULL; + char *loaded = NULL; + size_t need; + size_t off = 0; + char response_buf[OVERFLOW_REPLY_BYTES + 64]; + cJSON *parsed; + + memory_cleanup(); + if (memory_init(db_path) != 0) goto cleanup; + pad_a = malloc(PAD_A + 1); + pad_b = malloc(PAD_B + 1); + if (!pad_a || !pad_b) goto cleanup; + memset(pad_a, 'A', PAD_A); + pad_a[PAD_A] = '\0'; + memset(pad_b, 'B', PAD_B); + pad_b[PAD_B] = '\0'; + need = strlen(marker) + PAD_A + PAD_B + 128; + session_json = malloc(need); + if (!session_json) goto cleanup; + off = (size_t)snprintf(session_json, need, + "[{\"role\":\"user\",\"content\":\"%s%s\"},{\"role\":\"assistant\",\"content\":\"%s\"}]", + marker, pad_a, pad_b); + if (off == 0 || off >= need) goto cleanup; + if (session_save(session_id, session_json) != 0) goto cleanup; + + { + FILE *cf = fopen(config_path, "w"); + if (!cf) goto cleanup; + fprintf(cf, "[agent]\nmodel = \"test\"\nmax_context_messages = 40\n[memory]\npath = \"%s\"\n", + db_path); + fclose(cf); + } + { + char errbuf[256]; + if (config_load(config_path, &cfg, errbuf, sizeof(errbuf)) != 0) goto cleanup; + } + if (!cfg) goto cleanup; + + response_buf[0] = '\0'; + if (agent_run(cfg, session_id, "push over the limit", &overflow_reply_provider, NULL, 0, + response_buf, sizeof(response_buf)) != 0) { + fprintf(stderr, "FAIL: tests/test_agent.c: overflow agent_run failed\n"); + goto cleanup; + } + + loaded = malloc(LOAD_CAP); + if (!loaded) goto cleanup; + loaded[0] = '\0'; + if (session_load(session_id, loaded, LOAD_CAP) != 0) goto cleanup; + parsed = cJSON_Parse(loaded); + if (!parsed || !cJSON_IsArray(parsed)) { + if (parsed) cJSON_Delete(parsed); + fprintf(stderr, "FAIL: tests/test_agent.c: overflow session JSON unparseable\n"); + goto cleanup; + } + cJSON_Delete(parsed); + if (strstr(loaded, marker) == NULL) goto cleanup; + + /* Next turn must still see prior history (not wiped to empty []). */ + spy_roles_clear(); + response_buf[0] = '\0'; + if (agent_run(cfg, session_id, "still there?", &keep_history_provider, NULL, 0, + response_buf, sizeof(response_buf)) != 0) + goto cleanup; + { + int found = 0; + for (size_t j = 0; j < spy_message_count && j < SPY_SLOTS; j++) { + if (strstr(spy_content[j], marker) != NULL) { + found = 1; + break; + } + } + if (!found) goto cleanup; + } + failed = 0; +cleanup: + config_free(cfg); + free(session_json); + free(pad_a); + free(pad_b); + free(loaded); + remove(config_path); + remove(db_path); + memory_cleanup(); + return failed; +} + int main(void) { RUN(test_agent_run_with_stub_and_no_tools()); @@ -730,6 +899,7 @@ int main(void) RUN(test_context_compaction_when_history_exceeds_max()); RUN(test_local_offline_note_when_active_is_local()); RUN(test_local_offline_note_skipped_for_non_local()); + RUN(test_session_overflow_does_not_corrupt_history()); RUN(test_agent_provider_error_response()); RUN(test_agent_unknown_tool_continues()); printf("test_agent: all tests passed\n"); diff --git a/tests/test_memory.c b/tests/test_memory.c index 1a61b91..c653068 100644 --- a/tests/test_memory.c +++ b/tests/test_memory.c @@ -109,6 +109,28 @@ static int test_session_crud(void) return 0; } +/** Oversized session blobs must not be silently truncated into invalid JSON. */ +static int test_session_load_rejects_oversized(void) +{ + const char *path = "/tmp/shellclaw_test_memory_oversized.db"; + char big[2048]; + char small[64]; + size_t i; + remove(path); + ASSERT(memory_init(path) == 0); + big[0] = '['; + for (i = 1; i < sizeof(big) - 2; i++) + big[i] = 'x'; + big[sizeof(big) - 2] = ']'; + big[sizeof(big) - 1] = '\0'; + ASSERT(session_save("cli:big", big) == 0); + ASSERT(session_load("cli:big", small, sizeof(small)) == -1); + ASSERT(small[0] == '\0'); + memory_cleanup(); + remove(path); + return 0; +} + static int test_gateway_schema_new_db(void) { const char *path = "/tmp/shellclaw_test_gateway_schema.db"; @@ -198,6 +220,7 @@ int main(void) RUN(test_existing_db_preserved_on_open_failure()); RUN(test_session_list()); RUN(test_session_crud()); + RUN(test_session_load_rejects_oversized()); RUN(test_gateway_schema_new_db()); RUN(test_gateway_schema_migration_v01()); printf("test_memory: all tests passed\n"); From 8d8748cff4c73f25f33c5a04214c24d1f68fbffe Mon Sep 17 00:00:00 2001 From: Adrianno Esnarriaga Sereno Date: Sun, 13 Sep 2026 15:15:49 -0300 Subject: [PATCH 2/3] docs(agent,memory): changelog refuse session JSON truncation Refs: #70 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb7458f..32a4017 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to ShellClaw are documented here. Format follows [Keep a Cha ## [Unreleased] ### Fixed +- Session JSON that would exceed the 128 KiB cap is refused instead of truncated, so the next parse cannot wipe history. - Multi-round ReAct copies tool results into the in-flight message list so a later round cannot overwrite earlier outputs. - `memory_init` no longer deletes an existing SQLite DB when `sqlite3_open` fails (permissions or transient I/O). - Anthropic `content` parse fails closed when growing the text buffer or `tool_use` array cannot `realloc`, instead of copying against an inflated cap. From 1b54efdbb3dcaf7009331410941c979cfb752652 Mon Sep 17 00:00:00 2001 From: Adrianno Esnarriaga Sereno Date: Sun, 13 Sep 2026 15:29:10 -0300 Subject: [PATCH 3/3] fix(agent,memory): log refused session JSON and skip persist on oversized load Refuse copies now log session_id, len, and cap. session_load returns SESSION_LOAD_TOO_LARGE so a later small turn cannot overwrite the stored blob. Refs: #70 --- CHANGELOG.md | 2 +- src/core/agent.c | 61 ++++++++++++++++++++-------------- src/core/memory.c | 2 +- src/core/memory.h | 11 ++++++- tests/test_agent.c | 80 +++++++++++++++++++++++++++++++++++++++++++++ tests/test_memory.c | 2 +- 6 files changed, 130 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32a4017..2cdacfc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to ShellClaw are documented here. Format follows [Keep a Cha ## [Unreleased] ### Fixed -- Session JSON that would exceed the 128 KiB cap is refused instead of truncated, so the next parse cannot wipe history. +- Session JSON that would exceed the 128 KiB cap is refused instead of truncated, so the next parse cannot wipe history. An oversized stored blob is left in place (distinct `SESSION_LOAD_TOO_LARGE`) rather than replaced by a later small turn. - Multi-round ReAct copies tool results into the in-flight message list so a later round cannot overwrite earlier outputs. - `memory_init` no longer deletes an existing SQLite DB when `sqlite3_open` fails (permissions or transient I/O). - Anthropic `content` parse fails closed when growing the text buffer or `tool_use` array cannot `realloc`, instead of copying against an inflated cap. diff --git a/src/core/agent.c b/src/core/agent.c index a019640..34ef677 100644 --- a/src/core/agent.c +++ b/src/core/agent.c @@ -60,6 +60,26 @@ int agent_mutex_is_locked_for_test(void) #define SUMMARY_SOURCE_MAX (64 * 1024) #define SUMMARY_RESULT_MAX 4096 +/** Copy cJSON_PrintUnformatted output or refuse mid-JSON truncation (Refs: #70). */ +static int copy_printed_session_json(char *dst, size_t dst_size, char *printed, + const char *session_id) +{ + size_t len; + if (!printed) + return -1; + len = strlen(printed); + if (len >= dst_size) { + fprintf(stderr, + "agent: refuse session JSON truncation session_id=%s len=%zu cap=%zu\n", + session_id ? session_id : "", len, dst_size); + cJSON_free(printed); + return -1; + } + memcpy(dst, printed, len + 1); + cJSON_free(printed); + return 0; +} + static const char SUMMARIZE_SYSTEM[] = "Summarize the following conversation in one short paragraph. Output only the summary, no preamble."; /** Summarize oldest messages when over max_ctx; replace with one summary + trailing. */ @@ -128,16 +148,8 @@ static int compact_session_via_llm(const char *session_id, char *session_buf, si cJSON_Delete(root); char *printed = cJSON_PrintUnformatted(new_arr); cJSON_Delete(new_arr); - if (!printed) return -1; - size_t plen = strlen(printed); - /* Refuse mid-JSON truncation: a clipped payload corrupts the session and - * the next agent_run parse treats history as empty (permanent wipe). */ - if (plen >= session_buf_size) { - cJSON_free(printed); + if (copy_printed_session_json(session_buf, session_buf_size, printed, session_id) != 0) return -1; - } - memcpy(session_buf, printed, plen + 1); - cJSON_free(printed); session_save(session_id, session_buf); return 0; } @@ -284,7 +296,7 @@ static void free_tool_calls_copy(const provider_tool_call_t *copy, size_t n) /** Append user+assistant exchange to session JSON. Invalid or empty existing becomes []. */ static int append_exchange_to_session_json(const char *existing_json, const char *user_message, - const char *assistant_content, char *out_buf, size_t out_size) + const char *assistant_content, char *out_buf, size_t out_size, const char *session_id) { cJSON *arr = NULL; if (existing_json && existing_json[0] == '[') { @@ -310,17 +322,7 @@ static int append_exchange_to_session_json(const char *existing_json, const char } char *printed = cJSON_PrintUnformatted(arr); cJSON_Delete(arr); - if (!printed) return -1; - size_t len = strlen(printed); - /* Do not truncate: partial JSON saved via session_save is unparseable and - * wipes conversation history on the next agent_run (see agent_prepare_context). */ - if (len >= out_size) { - cJSON_free(printed); - return -1; - } - memcpy(out_buf, printed, len + 1); - cJSON_free(printed); - return 0; + return copy_printed_session_json(out_buf, out_size, printed, session_id); } static provider_tool_call_t *copy_tool_calls(const provider_tool_call_t *src, size_t n) @@ -365,6 +367,7 @@ typedef struct agent_run_ctx { int max_iter; int max_ctx; int ret; + int skip_session_persist; } agent_run_ctx_t; static void agent_oom_msg(agent_run_ctx_t *ctx) @@ -428,7 +431,10 @@ static int agent_prepare_context(agent_run_ctx_t *ctx) if (ctx->max_ctx <= 0 || ctx->max_ctx > MAX_HISTORY_MESSAGES) ctx->max_ctx = MAX_HISTORY_MESSAGES; ctx->session_buf[0] = '\0'; - session_load(ctx->session_id, ctx->session_buf, SESSION_JSON_MAX); + { + int load_rc = session_load(ctx->session_id, ctx->session_buf, SESSION_JSON_MAX); + ctx->skip_session_persist = (load_rc == SESSION_LOAD_TOO_LARGE); + } parsed = cJSON_Parse(ctx->session_buf); msg_count = (parsed && cJSON_IsArray(parsed)) ? cJSON_GetArraySize(parsed) : 0; if (parsed) @@ -470,11 +476,18 @@ static int agent_build_messages(agent_run_ctx_t *ctx) static void agent_persist_session(agent_run_ctx_t *ctx, const char *assistant_content) { - char *updated = malloc(SESSION_JSON_MAX); + char *updated; + if (ctx->skip_session_persist) { + fprintf(stderr, + "agent: skip session persist session_id=%s (stored blob exceeds cap)\n", + ctx->session_id ? ctx->session_id : ""); + return; + } + updated = malloc(SESSION_JSON_MAX); if (!updated) return; if (append_exchange_to_session_json(ctx->session_buf, ctx->user_message, assistant_content, - updated, SESSION_JSON_MAX) == 0) + updated, SESSION_JSON_MAX, ctx->session_id) == 0) session_save(ctx->session_id, updated); free(updated); } diff --git a/src/core/memory.c b/src/core/memory.c index 1213010..067b52c 100644 --- a/src/core/memory.c +++ b/src/core/memory.c @@ -249,7 +249,7 @@ int session_load(const char *session_id, char *messages_out, size_t max_len) * and agent_run would treat the session as empty history. */ if (n >= max_len) { sqlite3_finalize(stmt); - return -1; + return SESSION_LOAD_TOO_LARGE; } memcpy(messages_out, msg, n + 1); ret = 0; diff --git a/src/core/memory.h b/src/core/memory.h index 147e5a9..f8149ea 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -42,13 +42,22 @@ int memory_save(const char *key, const char *content, const char *metadata); */ int memory_recall(const char *query, char *results, size_t max_len, int limit); +/** + * Stored session JSON does not fit the caller buffer. messages_out is left empty. + * Distinct from "not found" so callers can skip persist instead of overwriting. + */ +#define SESSION_LOAD_TOO_LARGE (-2) + /** * Load session messages by session ID (e.g. "cli:default" or "telegram:123456789"). * * @param session_id Session identifier. * @param messages_out Output buffer for JSON array of messages; caller must free if allocated. * @param max_len Size of messages_out buffer (or 0 if messages_out is to be allocated by implementation). - * @return 0 on success, non-zero if not found or error. + * @return 0 on success, SESSION_LOAD_TOO_LARGE if the blob does not fit max_len, + * -1 if not found or error. + * + * Example: `if (session_load(id, buf, sizeof(buf)) == SESSION_LOAD_TOO_LARGE) skip_save;` */ int session_load(const char *session_id, char *messages_out, size_t max_len); diff --git a/tests/test_agent.c b/tests/test_agent.c index f8c312d..78cb14c 100644 --- a/tests/test_agent.c +++ b/tests/test_agent.c @@ -887,6 +887,85 @@ static int test_session_overflow_does_not_corrupt_history(void) return failed; } +/** + * A stored blob larger than SESSION_JSON_MAX must not be replaced by a later + * small turn: session_load refuse leaves an empty buffer, and persist must not + * treat that as a new empty session. + */ +static int test_oversized_stored_session_not_wiped_by_small_turn(void) +{ + int failed = 1; + config_t *cfg = NULL; + const char *db_path = "build/test_agent_oversize_load.db"; + const char *config_path = "build/test_agent_oversize_load.toml"; + const char *session_id = "cli:oversize-load"; + const char *marker = "OVERSIZE_LOAD_MARKER_xyz"; + enum { PAD = 128 * 1024, LOAD_CAP = 160 * 1024 }; + char *pad = NULL; + char *session_json = NULL; + char *loaded = NULL; + size_t need; + size_t off = 0; + char response_buf[256]; + cJSON *parsed; + + memory_cleanup(); + if (memory_init(db_path) != 0) goto cleanup; + pad = malloc(PAD + 1); + if (!pad) goto cleanup; + memset(pad, 'Z', PAD); + pad[PAD] = '\0'; + need = strlen(marker) + PAD + 128; + session_json = malloc(need); + if (!session_json) goto cleanup; + off = (size_t)snprintf(session_json, need, + "[{\"role\":\"user\",\"content\":\"%s%s\"}]", marker, pad); + if (off == 0 || off >= need) goto cleanup; + if (session_save(session_id, session_json) != 0) goto cleanup; + { + FILE *cf = fopen(config_path, "w"); + if (!cf) goto cleanup; + fprintf(cf, "[agent]\nmodel = \"test\"\n[memory]\npath = \"%s\"\n", db_path); + fclose(cf); + } + { + char errbuf[256]; + if (config_load(config_path, &cfg, errbuf, sizeof(errbuf)) != 0) goto cleanup; + } + if (!cfg) goto cleanup; + response_buf[0] = '\0'; + if (agent_run(cfg, session_id, "tiny", &persist_reply_provider, NULL, 0, + response_buf, sizeof(response_buf)) != 0) { + fprintf(stderr, "FAIL: tests/test_agent.c: oversize-load agent_run failed\n"); + goto cleanup; + } + loaded = malloc(LOAD_CAP); + if (!loaded) goto cleanup; + loaded[0] = '\0'; + if (session_load(session_id, loaded, LOAD_CAP) != 0) goto cleanup; + parsed = cJSON_Parse(loaded); + if (!parsed || !cJSON_IsArray(parsed)) { + if (parsed) cJSON_Delete(parsed); + fprintf(stderr, "FAIL: tests/test_agent.c: oversize stored session wiped or corrupt\n"); + goto cleanup; + } + cJSON_Delete(parsed); + if (strstr(loaded, marker) == NULL) { + fprintf(stderr, "FAIL: tests/test_agent.c: oversize stored session missing marker\n"); + goto cleanup; + } + failed = 0; +cleanup: + config_free(cfg); + free(pad); + free(session_json); + free(loaded); + remove(config_path); + remove(db_path); + memory_cleanup(); + return failed; +} + int main(void) { RUN(test_agent_run_with_stub_and_no_tools()); @@ -900,6 +979,7 @@ int main(void) RUN(test_local_offline_note_when_active_is_local()); RUN(test_local_offline_note_skipped_for_non_local()); RUN(test_session_overflow_does_not_corrupt_history()); + RUN(test_oversized_stored_session_not_wiped_by_small_turn()); RUN(test_agent_provider_error_response()); RUN(test_agent_unknown_tool_continues()); printf("test_agent: all tests passed\n"); diff --git a/tests/test_memory.c b/tests/test_memory.c index c653068..8b0c7b5 100644 --- a/tests/test_memory.c +++ b/tests/test_memory.c @@ -124,7 +124,7 @@ static int test_session_load_rejects_oversized(void) big[sizeof(big) - 2] = ']'; big[sizeof(big) - 1] = '\0'; ASSERT(session_save("cli:big", big) == 0); - ASSERT(session_load("cli:big", small, sizeof(small)) == -1); + ASSERT(session_load("cli:big", small, sizeof(small)) == SESSION_LOAD_TOO_LARGE); ASSERT(small[0] == '\0'); memory_cleanup(); remove(path);