Skip to content

Implementation of sc-memory based on disk space - #495

Open
bogdansemchenko wants to merge 3 commits into
ostis-ai:mainfrom
bogdansemchenko:feat/sc_memory_dump
Open

Implementation of sc-memory based on disk space#495
bogdansemchenko wants to merge 3 commits into
ostis-ai:mainfrom
bogdansemchenko:feat/sc_memory_dump

Conversation

@bogdansemchenko

@bogdansemchenko bogdansemchenko commented May 22, 2025

Copy link
Copy Markdown

Important

Implements disk-based sc-memory with L1/L2 caching, asynchronous I/O, and buffered operations for enhanced efficiency and scalability.

  • Memory Management:
    • Introduces L1 and L2 caches in sc_storage.c for efficient memory management using DRAM and SSD/NVMe.
    • Implements _sc_l1_cache_init(), _sc_l1_cache_destroy(), _sc_l1_cache_add(), _sc_l1_cache_get(), _sc_l2_cache_init(), _sc_l2_cache_destroy(), _sc_l2_cache_add(), _sc_l2_cache_get() for cache operations.
    • Adds sc_segment_buffer for buffering operations in sc_storage.c.
  • Asynchronous and Parallel I/O:
    • Implements write_segment_async() and write_segments_parallel() in sc_fs_memory.c for asynchronous and parallel segment writing.
    • Utilizes libaio and pthread for I/O operations.
  • Buffering and Operations:
    • Adds _sc_storage_add_operation() and _sc_storage_flush_buffer() in sc_storage.c to handle buffered operations.
    • Supports operations like SC_OP_NODE_NEW, SC_OP_ARC_NEW, SC_OP_LINK_NEW, SC_OP_SET_LINK_CONTENT, SC_OP_ERASE_ELEMENT.
  • File System Integration:
    • Modifies sc_fs_memory.c to handle file-based storage, including reading and writing segments to disk.
    • Updates sc_file_system.c and sc_io.h for file operations.
  • Miscellaneous:
    • Updates sc_storage_initialize() and sc_storage_shutdown() in sc_storage.c to integrate new memory management features.
    • Adds new data structures and functions in sc_storage_private.h for internal storage management.

This description was created by Ellipsis for fc6f7f5. You can customize this summary. It will automatically update as commits are pushed.

@ellipsis-dev ellipsis-dev Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Changes requested ❌

Reviewed everything up to fc6f7f5 in 3 minutes and 18 seconds. Click for details.
  • Reviewed 4739 lines of code in 12 files
  • Skipped 0 files when reviewing.
  • Skipped posting 4 draft comments. View those below.
  • Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
1. sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_file_system.c:92
  • Draft comment:
    BUG: The allocated 'command' buffer is not initialized before using strcat. This may lead to undefined behavior. Use strcpy or zero‐initialize the buffer.
  • Reason this comment was not posted:
    Comment was on unchanged code.
2. sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_file_system.c:205
  • Draft comment:
    BUG: 'char_result' is used without allocation. sc_str_cpy is called on an uninitialized pointer.
  • Reason this comment was not posted:
    Comment was on unchanged code.
3. sc-memory/sc-core/src/sc-store/sc_segment.h:35
  • Draft comment:
    Typo in the parameter comment: "Number of created instance in sc-memory" should be "Number of created instances in sc-memory".
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.
4. sc-memory/sc-core/src/sc-store/sc_storage.c:660
  • Draft comment:
    Typographical note: The identifier 'null_ptr' in this new line may be a typo. If the intention is to use a null pointer literal, consider using 'nullptr' (in C++) or 'NULL' (in C) depending on your code's conventions.
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.

Workflow ID: wflow_Q1jJLymj506I50iT

You can customize Ellipsis by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.

if (!cache->tail)
cache->tail = node;
cache->size++;
sc_hash_table_insert(cache->segments, &seg_num, node);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BUG: In _sc_l1_cache_add, using &seg_num as key for hash table insertion is unsafe. Use a stable key (e.g. GINT_TO_POINTER) instead.

Suggested change
sc_hash_table_insert(cache->segments, &seg_num, node);
sc_hash_table_insert(cache->segments, GUINT_TO_POINTER(seg_num), node);

io_destroy(ctx);
close(fd);

sc_hash_table_insert(cache->segments, &seg_num, (void *)(intptr_t)entry.offset);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BUG: In _sc_l2_cache_add, the key is inserted using &seg_num, a pointer to a local variable. Use a stable representation (e.g. GINT_TO_POINTER(seg_num)).

@NikitaZotov
NikitaZotov marked this pull request as draft July 14, 2025 14:53
@bogdansemchenko
bogdansemchenko marked this pull request as ready for review October 9, 2025 11:00
Comment thread sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_fs_memory.c
if (!cache->tail)
cache->tail = node;
cache->size++;
sc_hash_table_insert(cache->segments, &seg_num, node);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 Bug: LRU cache inserts dangling pointer to stack key into hash table

_sc_l1_cache_add calls sc_hash_table_insert(cache->segments, &seg_num, node) and _sc_l2_cache_add calls sc_hash_table_insert(cache->segments, &seg_num, ...), passing the address of the seg_num function parameter. The GLib int64 hash table stores this key pointer, but it becomes dangling as soon as the function returns. Subsequent _sc_l1_cache_get/_sc_l2_cache_get lookups invoke g_int64_equal, which dereferences the stored dangling pointer — reading freed stack memory (undefined behavior), leading to unreliable cache hits/misses and potential crashes. Insert the address of a stably-stored key instead (e.g. &node->segment_num for L1, or a heap-allocated key for L2).

node->segment_num was already set from seg_num and outlives the entry:

// L1: key must live as long as the entry
sc_hash_table_insert(cache->segments, &node->segment_num, node);
  • Apply fix

Check the box to apply the fix or reply for a change | Was this helpful? React with 👍 / 👎

Comment on lines +290 to +297
storage->segments = malloc(storage->segments_count * sizeof(sc_segment*));
if (!storage->segments) {
sc_fs_memory_error("Failed to allocate memory for segments array");
close(fd);
return SC_FS_MEMORY_READ_ERROR;
}
for (sc_addr_seg i = 0; i < storage->segments_count; ++i)
{
sc_addr_seg const num = i;
sc_segment * seg = sc_segment_new(i + 1);
storage->segments[i] = seg;

for (sc_addr_seg j = 0; j < SC_SEGMENT_ELEMENTS_COUNT; ++j)
{
if (sc_io_channel_read_chars(segments_channel, (sc_char *)&seg->elements[j], element_size, &read_bytes, null_ptr)
!= SC_FS_IO_STATUS_NORMAL
|| read_bytes != element_size)
{
storage->segments_count = num;
sc_fs_memory_error("Error while sc-element %d in sc-segment %d reading", j, i);
goto error;
storage->segments[i] = null_ptr;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 Bug: Segment load overwrites/undersizes storage->segments array

_sc_fs_memory_load_sc_memory_segments does storage->segments = malloc(storage->segments_count * sizeof(sc_segment*)), overwriting the array already allocated in sc_storage_initialize as sc_mem_new(sc_segment*, params->max_loaded_segments) (memory leak), and sizing it to segments_count rather than max_segments_count. After load, code such as _sc_storage_get_new_segment writes storage->segments[storage->segments_count] up to max_segments_count, causing a heap-buffer overflow past the smaller array. Keep the pre-allocated max_loaded_segments-sized array and only fill entries, rather than reallocating to segments_count.

Fix:

// Do not reallocate; reuse the array allocated in sc_storage_initialize
// (sized to max_loaded_segments). Just reset the used entries:
for (sc_addr_seg i = 0; i < storage->segments_count; ++i)
  storage->segments[i] = null_ptr;
  • Apply fix

Check the box to apply the fix or reply for a change | Was this helpful? React with 👍 / 👎

cache->capacity = capacity;
sc_monitor_init(&cache->monitor);

int fd = open(cache->cache_path, O_RDWR | O_CREAT | O_DIRECT, 0666);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Bug: O_DIRECT I/O uses unaligned length/offset, will fail with EINVAL

The L2 cache opens its file with O_DIRECT and then writes/reads with unaligned parameters: the 16-byte metadata entry is written with a plain write(), and segment payloads are placed at entry.offset = offset + sizeof(entry) (16-byte granularity). O_DIRECT requires the buffer address, file offset, and transfer length to all be aligned to the device block size (typically 512/4096). Unaligned transfers return EINVAL, so write/io_submit/pread on these fds will consistently fail and the L2 cache will not function. Either drop O_DIRECT or align every offset and length to PAGE_SIZE.

Was this helpful? React with 👍 / 👎

Comment on lines +306 to +320
while (lseek(cache_fd, 0, SEEK_CUR) < cache_size) {
struct { sc_addr_seg num; off_t offset; } entry;
if (read(cache_fd, &entry, sizeof(entry)) != sizeof(entry)) {
sc_fs_memory_warning("Failed to read L2 cache entry");
break;
}

// needed for sc-template search
if (!is_no_deprecated_segments)
{
seg->elements[j].incoming_arcs_count = 1;
seg->elements[j].outgoing_arcs_count = 1;
sc_segment* seg = aligned_alloc(PAGE_SIZE, sizeof(sc_segment));
if (!seg) {
sc_fs_memory_warning("Failed to allocate segment for L2 cache");
continue;
}
if (pread(cache_fd, seg, sizeof(sc_segment), entry.offset) != sizeof(sc_segment)) {
sc_fs_memory_warning("Failed to read segment from L2 cache");
free(seg);
continue;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Bug: L2 cache file read loop does not skip over segment payloads

On disk the L2 cache is laid out as [entry(16B)][segment][entry(16B)][segment]... because _sc_l2_cache_add writes the 16-byte entry sequentially and then writes the segment at entry.offset = pos+16. The load loop in _sc_fs_memory_load_sc_memory_segments reads a 16-byte entry, then preads the segment at entry.offset, but only advances the file position by 16 bytes. The next read therefore interprets the segment payload as the next entry, corrupting all subsequent cache reads. After reading an entry, seek past the segment payload (lseek(cache_fd, sizeof(sc_segment), SEEK_CUR)) before reading the next entry.

Was this helpful? React with 👍 / 👎

return SC_FS_MEMORY_READ_ERROR;
}

storage->segments = malloc(storage->segments_count * sizeof(sc_segment*));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Bug: Allocator mismatch: aligned_alloc/malloc segments freed via sc_mem_free/sc_segment_free

Segments and the segments array loaded here are created with malloc/aligned_alloc, but elsewhere they are released with the project allocator: sc_storage_shutdown calls sc_segment_free(segment) on each and sc_mem_free(storage->segments). If sc_mem_new/sc_mem_free do not map directly to libc malloc/free (e.g. GLib slice or an instrumented allocator), freeing an aligned_alloc/malloc pointer through them is undefined behavior and can corrupt the heap. Use one consistent allocator for segments and the segments array across creation and destruction.

Was this helpful? React with 👍 / 👎

Comment on lines +92 to +101
if (!segment && op->type != SC_OP_ERASE_ELEMENT)
{
segment = sc_segment_new(op->addr.seg);
sc_monitor_acquire_write(&storage->segments_monitor);
storage->segments[op->addr.seg - 1] = segment;
if (op->addr.seg > storage->segments_count)
storage->segments_count = op->addr.seg;
sc_monitor_release_write(&storage->segments_monitor);
_sc_l1_cache_add(storage->l1_cache, op->addr.seg, segment);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Bug: _sc_storage_flush_buffer lacks upper-bound check on op->addr.seg

In the buffer-flush path, when no segment is found and the op is not an erase, it does storage->segments[op->addr.seg - 1] = segment without validating op->addr.seg against storage->max_segments_count (the earlier lookup only guards op->addr.seg <= storage->segments_count). A seg value of 0 yields segments[-1], and a value beyond max_segments_count writes past the array — both out-of-bounds writes. Add a bounds check (op->addr.seg >= 1 && op->addr.seg <= storage->max_segments_count) before indexing.

Was this helpful? React with 👍 / 👎

@@ -1,8 +1,8 @@
/*

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Quality: License headers and indentation mangled across files

Large portions of sc_file_system.c, sc_fs_memory.c and the private headers were reindented (2-space to 1-space) and the MIT license comment blocks lost their leading * alignment, producing ~124/-124 no-op churn that obscures the real changes and will conflict with the project style. Several files also lost their trailing newline. Revert the whitespace/header reformatting so the diff reflects only functional changes.

Was this helpful? React with 👍 / 👎

@gitar-bot

gitar-bot Bot commented Jul 19, 2026

Copy link
Copy Markdown
Code Review 🚫 Blocked 1 resolved / 8 findings

Implements disk-based sc-memory with L1/L2 caching and asynchronous I/O, but is blocked by critical memory safety issues, including dangling pointers in LRU cache inserts and heap corruption via allocator mismatches. Additionally, O_DIRECT alignment violations and improper segment loading logic prevent correct functional operation.

🚨 Bug: LRU cache inserts dangling pointer to stack key into hash table

📄 sc-memory/sc-core/src/sc-store/sc_storage.c:1767 📄 sc-memory/sc-core/src/sc-store/sc_storage.c:1891

_sc_l1_cache_add calls sc_hash_table_insert(cache->segments, &seg_num, node) and _sc_l2_cache_add calls sc_hash_table_insert(cache->segments, &seg_num, ...), passing the address of the seg_num function parameter. The GLib int64 hash table stores this key pointer, but it becomes dangling as soon as the function returns. Subsequent _sc_l1_cache_get/_sc_l2_cache_get lookups invoke g_int64_equal, which dereferences the stored dangling pointer — reading freed stack memory (undefined behavior), leading to unreliable cache hits/misses and potential crashes. Insert the address of a stably-stored key instead (e.g. &node->segment_num for L1, or a heap-allocated key for L2).

node->segment_num was already set from seg_num and outlives the entry
// L1: key must live as long as the entry
sc_hash_table_insert(cache->segments, &node->segment_num, node);
🚨 Bug: Segment load overwrites/undersizes storage->segments array

📄 sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_fs_memory.c:290-297

_sc_fs_memory_load_sc_memory_segments does storage->segments = malloc(storage->segments_count * sizeof(sc_segment*)), overwriting the array already allocated in sc_storage_initialize as sc_mem_new(sc_segment*, params->max_loaded_segments) (memory leak), and sizing it to segments_count rather than max_segments_count. After load, code such as _sc_storage_get_new_segment writes storage->segments[storage->segments_count] up to max_segments_count, causing a heap-buffer overflow past the smaller array. Keep the pre-allocated max_loaded_segments-sized array and only fill entries, rather than reallocating to segments_count.

Fix
// Do not reallocate; reuse the array allocated in sc_storage_initialize
// (sized to max_loaded_segments). Just reset the used entries:
for (sc_addr_seg i = 0; i < storage->segments_count; ++i)
  storage->segments[i] = null_ptr;
⚠️ Bug: O_DIRECT I/O uses unaligned length/offset, will fail with EINVAL

📄 sc-memory/sc-core/src/sc-store/sc_storage.c:1807 📄 sc-memory/sc-core/src/sc-store/sc_storage.c:1836 📄 sc-memory/sc-core/src/sc-store/sc_storage.c:1845 📄 sc-memory/sc-core/src/sc-store/sc_storage.c:1867 📄 sc-memory/sc-core/src/sc-store/sc_storage.c:1908

The L2 cache opens its file with O_DIRECT and then writes/reads with unaligned parameters: the 16-byte metadata entry is written with a plain write(), and segment payloads are placed at entry.offset = offset + sizeof(entry) (16-byte granularity). O_DIRECT requires the buffer address, file offset, and transfer length to all be aligned to the device block size (typically 512/4096). Unaligned transfers return EINVAL, so write/io_submit/pread on these fds will consistently fail and the L2 cache will not function. Either drop O_DIRECT or align every offset and length to PAGE_SIZE.

⚠️ Bug: L2 cache file read loop does not skip over segment payloads

📄 sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_fs_memory.c:306-320 📄 sc-memory/sc-core/src/sc-store/sc_storage.c:1844-1846 📄 sc-memory/sc-core/src/sc-store/sc_storage.c:1867

On disk the L2 cache is laid out as [entry(16B)][segment][entry(16B)][segment]... because _sc_l2_cache_add writes the 16-byte entry sequentially and then writes the segment at entry.offset = pos+16. The load loop in _sc_fs_memory_load_sc_memory_segments reads a 16-byte entry, then preads the segment at entry.offset, but only advances the file position by 16 bytes. The next read therefore interprets the segment payload as the next entry, corrupting all subsequent cache reads. After reading an entry, seek past the segment payload (lseek(cache_fd, sizeof(sc_segment), SEEK_CUR)) before reading the next entry.

⚠️ Bug: Allocator mismatch: aligned_alloc/malloc segments freed via sc_mem_free/sc_segment_free

📄 sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_fs_memory.c:290 📄 sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_fs_memory.c:312 📄 sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_fs_memory.c:338 📄 sc-memory/sc-core/src/sc-store/sc_storage.c:262 📄 sc-memory/sc-core/src/sc-store/sc_storage.c:280

Segments and the segments array loaded here are created with malloc/aligned_alloc, but elsewhere they are released with the project allocator: sc_storage_shutdown calls sc_segment_free(segment) on each and sc_mem_free(storage->segments). If sc_mem_new/sc_mem_free do not map directly to libc malloc/free (e.g. GLib slice or an instrumented allocator), freeing an aligned_alloc/malloc pointer through them is undefined behavior and can corrupt the heap. Use one consistent allocator for segments and the segments array across creation and destruction.

⚠️ Bug: _sc_storage_flush_buffer lacks upper-bound check on op->addr.seg

📄 sc-memory/sc-core/src/sc-store/sc_storage.c:92-101

In the buffer-flush path, when no segment is found and the op is not an erase, it does storage->segments[op->addr.seg - 1] = segment without validating op->addr.seg against storage->max_segments_count (the earlier lookup only guards op->addr.seg <= storage->segments_count). A seg value of 0 yields segments[-1], and a value beyond max_segments_count writes past the array — both out-of-bounds writes. Add a bounds check (op->addr.seg >= 1 && op->addr.seg <= storage->max_segments_count) before indexing.

💡 Quality: License headers and indentation mangled across files

📄 sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_file_system.c:1-15 📄 sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_fs_memory.c:1-8

Large portions of sc_file_system.c, sc_fs_memory.c and the private headers were reindented (2-space to 1-space) and the MIT license comment blocks lost their leading * alignment, producing ~124/-124 no-op churn that obscures the real changes and will conflict with the project style. Several files also lost their trailing newline. Revert the whitespace/header reformatting so the diff reflects only functional changes.

✅ 1 resolved
Bug: write_segments_parallel overwrites thread handles, leaks/joins garbage

📄 sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_fs_memory.c:168-182
The inner for (j...) loop calls pthread_create(&threads[i], ...) repeatedly for the same slot i, so every thread except the last one created in that slot has its handle discarded. Those threads are never joined, yet segments is free()d right after the join loop (line 483) while they may still be writing — a use-after-free and data race. Additionally, when num_segments < NUM_THREADS the outer loop breaks early, leaving some threads[i] uninitialized, and the final join loop calls pthread_join on all NUM_THREADS entries including those garbage handles (undefined behavior). Use a flat array of pthread_t threads[num_segments] (or a dynamically allocated one), create exactly one thread per segment, and join exactly the threads that were successfully created.

🤖 Prompt for agents
Code Review: Implements disk-based sc-memory with L1/L2 caching and asynchronous I/O, but is blocked by critical memory safety issues, including dangling pointers in LRU cache inserts and heap corruption via allocator mismatches. Additionally, O_DIRECT alignment violations and improper segment loading logic prevent correct functional operation.

1. 🚨 Bug: LRU cache inserts dangling pointer to stack key into hash table
   Files: sc-memory/sc-core/src/sc-store/sc_storage.c:1767, sc-memory/sc-core/src/sc-store/sc_storage.c:1891

   `_sc_l1_cache_add` calls `sc_hash_table_insert(cache->segments, &seg_num, node)` and `_sc_l2_cache_add` calls `sc_hash_table_insert(cache->segments, &seg_num, ...)`, passing the address of the `seg_num` function parameter. The GLib int64 hash table stores this key pointer, but it becomes dangling as soon as the function returns. Subsequent `_sc_l1_cache_get`/`_sc_l2_cache_get` lookups invoke `g_int64_equal`, which dereferences the stored dangling pointer — reading freed stack memory (undefined behavior), leading to unreliable cache hits/misses and potential crashes. Insert the address of a stably-stored key instead (e.g. `&node->segment_num` for L1, or a heap-allocated key for L2).

   Fix (node->segment_num was already set from seg_num and outlives the entry):
   // L1: key must live as long as the entry
   sc_hash_table_insert(cache->segments, &node->segment_num, node);

2. 🚨 Bug: Segment load overwrites/undersizes storage->segments array
   Files: sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_fs_memory.c:290-297

   `_sc_fs_memory_load_sc_memory_segments` does `storage->segments = malloc(storage->segments_count * sizeof(sc_segment*))`, overwriting the array already allocated in `sc_storage_initialize` as `sc_mem_new(sc_segment*, params->max_loaded_segments)` (memory leak), and sizing it to `segments_count` rather than `max_segments_count`. After load, code such as `_sc_storage_get_new_segment` writes `storage->segments[storage->segments_count]` up to `max_segments_count`, causing a heap-buffer overflow past the smaller array. Keep the pre-allocated `max_loaded_segments`-sized array and only fill entries, rather than reallocating to `segments_count`.

   Fix:
   // Do not reallocate; reuse the array allocated in sc_storage_initialize
   // (sized to max_loaded_segments). Just reset the used entries:
   for (sc_addr_seg i = 0; i < storage->segments_count; ++i)
     storage->segments[i] = null_ptr;

3. ⚠️ Bug: O_DIRECT I/O uses unaligned length/offset, will fail with EINVAL
   Files: sc-memory/sc-core/src/sc-store/sc_storage.c:1807, sc-memory/sc-core/src/sc-store/sc_storage.c:1836, sc-memory/sc-core/src/sc-store/sc_storage.c:1845, sc-memory/sc-core/src/sc-store/sc_storage.c:1867, sc-memory/sc-core/src/sc-store/sc_storage.c:1908

   The L2 cache opens its file with `O_DIRECT` and then writes/reads with unaligned parameters: the 16-byte metadata entry is written with a plain `write()`, and segment payloads are placed at `entry.offset = offset + sizeof(entry)` (16-byte granularity). O_DIRECT requires the buffer address, file offset, and transfer length to all be aligned to the device block size (typically 512/4096). Unaligned transfers return `EINVAL`, so `write`/`io_submit`/`pread` on these fds will consistently fail and the L2 cache will not function. Either drop `O_DIRECT` or align every offset and length to PAGE_SIZE.

4. ⚠️ Bug: L2 cache file read loop does not skip over segment payloads
   Files: sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_fs_memory.c:306-320, sc-memory/sc-core/src/sc-store/sc_storage.c:1844-1846, sc-memory/sc-core/src/sc-store/sc_storage.c:1867

   On disk the L2 cache is laid out as `[entry(16B)][segment][entry(16B)][segment]...` because `_sc_l2_cache_add` writes the 16-byte entry sequentially and then writes the segment at `entry.offset = pos+16`. The load loop in `_sc_fs_memory_load_sc_memory_segments` reads a 16-byte entry, then `pread`s the segment at `entry.offset`, but only advances the file position by 16 bytes. The next `read` therefore interprets the segment payload as the next entry, corrupting all subsequent cache reads. After reading an entry, seek past the segment payload (`lseek(cache_fd, sizeof(sc_segment), SEEK_CUR)`) before reading the next entry.

5. ⚠️ Bug: Allocator mismatch: aligned_alloc/malloc segments freed via sc_mem_free/sc_segment_free
   Files: sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_fs_memory.c:290, sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_fs_memory.c:312, sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_fs_memory.c:338, sc-memory/sc-core/src/sc-store/sc_storage.c:262, sc-memory/sc-core/src/sc-store/sc_storage.c:280

   Segments and the segments array loaded here are created with `malloc`/`aligned_alloc`, but elsewhere they are released with the project allocator: `sc_storage_shutdown` calls `sc_segment_free(segment)` on each and `sc_mem_free(storage->segments)`. If `sc_mem_new`/`sc_mem_free` do not map directly to libc `malloc`/`free` (e.g. GLib slice or an instrumented allocator), freeing an `aligned_alloc`/`malloc` pointer through them is undefined behavior and can corrupt the heap. Use one consistent allocator for segments and the segments array across creation and destruction.

6. ⚠️ Bug: _sc_storage_flush_buffer lacks upper-bound check on op->addr.seg
   Files: sc-memory/sc-core/src/sc-store/sc_storage.c:92-101

   In the buffer-flush path, when no segment is found and the op is not an erase, it does `storage->segments[op->addr.seg - 1] = segment` without validating `op->addr.seg` against `storage->max_segments_count` (the earlier lookup only guards `op->addr.seg <= storage->segments_count`). A seg value of 0 yields `segments[-1]`, and a value beyond `max_segments_count` writes past the array — both out-of-bounds writes. Add a bounds check (`op->addr.seg >= 1 && op->addr.seg <= storage->max_segments_count`) before indexing.

7. 💡 Quality: License headers and indentation mangled across files
   Files: sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_file_system.c:1-15, sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_fs_memory.c:1-8

   Large portions of `sc_file_system.c`, `sc_fs_memory.c` and the private headers were reindented (2-space to 1-space) and the MIT license comment blocks lost their leading ` * ` alignment, producing ~124/-124 no-op churn that obscures the real changes and will conflict with the project style. Several files also lost their trailing newline. Revert the whitespace/header reformatting so the diff reflects only functional changes.

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant