changeset 255:5ec271d612ae

[dowa] Enforce arena ownership Co-authored-by: Copilot <[email protected]>
author MrJuneJune <me@mrjunejune.com>
date Tue, 04 Aug 2026 15:12:18 -0700
parents 2b6e732087ff
children 30c2196d03d4
files .claude/skills/zenbu-bazel-c/SKILL.md mrjunejune/create_html_from_md.c s3/s3_uploader.c
diffstat 3 files changed, 24 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/.claude/skills/zenbu-bazel-c/SKILL.md	Tue Aug 04 15:12:09 2026 -0700
+++ b/.claude/skills/zenbu-bazel-c/SKILL.md	Tue Aug 04 15:12:18 2026 -0700
@@ -26,6 +26,10 @@
 
 Avoid broad searches in `third_party/`, Bazel output directories, virtualenvs, `node_modules`, and generated bundles unless the task specifically requires them.
 
+For cross-codebase audits, inspect `BUILD` files and Bazel dependencies first.
+Use the dependency graph to identify actual first-party consumers before broad
+text search, then audit only those targets and their transitive owners.
+
 ## Build and test commands
 
 Use Bazel targets from the repository root:
@@ -58,6 +62,16 @@
   `TRUE`/`FALSE` in first-party C. Do not introduce `<stdint.h>` `_t` aliases
   or `<stdbool.h>` `bool` when a Dowa type already expresses the value.
 - C code generally uses `Dowa_Arena` for request-scoped allocations.
+- An arena owns every pointer returned by `Dowa_Arena_Allocate`, aligned arena
+  allocation, arena string helpers, and arena array/hash-map macros. Never pass
+  those pointers to libc `free()`, `Dowa_Free`, or an element-level destroy
+  function.
+- Release a locally created arena exactly once with `Dowa_Arena_Free()` on
+  every return path. Do not release a borrowed request arena; its owner must
+  free it.
+- Prefer `Dowa_Free` over raw libc `free()` for separately heap-allocated
+  first-party pointers. Keep the allocation source explicit when heap and arena
+  ownership coexist in one function.
 - HTTP responses are usually `Seobeo_Request_Entry *resp = NULL` maps filled with `Dowa_HashMap_Push_Arena`.
 - Route handlers return response maps; streaming handlers receive `Seobeo_Handle *` directly.
 - Existing code favors explicit status/content-type/body response fields.
--- a/mrjunejune/create_html_from_md.c	Tue Aug 04 15:12:09 2026 -0700
+++ b/mrjunejune/create_html_from_md.c	Tue Aug 04 15:12:18 2026 -0700
@@ -126,10 +126,9 @@
     Blog_Metadata meta = Parse_Blog_Frontmatter(md_file);
     char *md = markdown_to_html(meta.content);
     char *ssr_body = Dowa_Arena_Allocate(arena, buffer_sizes);
-    char *final_body = Dowa_Arena_Allocate(arena, buffer_sizes);
     snprintf(ssr_body, buffer_sizes, BLOG_HTML, meta.description, meta.title, md);
+    markdown_free(md);
 
-    int open_flags = O_RDWR | O_CREAT | O_EXCL;
     size_t md_file_path_length = strlen(md_file_path);
     md_file_path_length -= 3; // html
     char *new_file_path = Dowa_Arena_Allocate(arena, 1024);
@@ -137,11 +136,14 @@
 
     FILE *new_file_fd = fopen(new_file_path, "w+");
     if (!new_file_fd)
-      return;
+    {
+      Dowa_Arena_Free(arena);
+      return 1;
+    }
     printf("Saving to %s...\n", new_file_path);
     fwrite(ssr_body, 1, buffer_sizes, new_file_fd);
     fclose(new_file_fd);
   }
+  Dowa_Arena_Free(arena);
   return 0;
 }
-
--- a/s3/s3_uploader.c	Tue Aug 04 15:12:09 2026 -0700
+++ b/s3/s3_uploader.c	Tue Aug 04 15:12:18 2026 -0700
@@ -73,12 +73,12 @@
                                             const char *content_type)
 {
   S3_Result result = {0};
-  result.p_arena = Dowa_Arena_Create(S3_RESULT_ARENA_SIZE);
 
   size_t file_size = 0;
   uint8 *file_data = s3__load_file(local_path, &file_size);
   if (!file_data)
   {
+    result.p_arena = Dowa_Arena_Create(S3_RESULT_ARENA_SIZE);
     result.success = FALSE;
     result.status_code = 0;
     result.error_message = Dowa_String_Copy_Arena("Failed to read file", result.p_arena);
@@ -86,7 +86,7 @@
   }
 
   result = S3_Upload_Data(p_config, file_data, file_size, s3_key, content_type);
-  free(file_data);
+  Dowa_Free(file_data);
 
   return result;
 }
@@ -706,7 +706,7 @@
   s3__hmac_sha256(k_service, 32,
                   (const uint8 *)S3_AWS4_REQUEST, strlen(S3_AWS4_REQUEST), out);
 
-  free(k_secret);
+  Dowa_Free(k_secret);
 }
 
 static char *s3__build_authorization_header(const char *access_key,
@@ -777,7 +777,7 @@
 
   if (read != (size_t)size)
   {
-    free(data);
+    Dowa_Free(data);
     *p_size = 0;
     return NULL;
   }