Skip to content

Conversation

@AlexJuca
Copy link
Owner

This PR fixes several severe memory-safety issues in our hash-table implementation.

Both set_value() and get_value() contained bugs capable of causing:

  • heap corruption
  • dangling pointers
  • partial writes
  • data loss
  • crashes during normal operations

This PR rewrites the critical sections to ensure correctness, atomicity, and safe memory ownership.
Key Fixes

Improvements to set_value()

  • Ensures old values are only freed after new allocations succeed
  • Fixes incorrect cleanup for new vs existing entries
  • Fixes bucket corruption on value-allocation failure
  • Prevents double-free conditions
  • Adds zero-length value support
  • Makes update logic fully atomic

Improvements to get_value()

  • Corrects malloc(sizeof(pointer)) → malloc(sizeof(value_entry_t))
  • Performs a deep copy of value bytes
  • Removes use-after-free where out->ptr was freed before return
  • Ensures safe error handling on allocation failures
  • Prevents callers from mutating internal storage

What is the outcome of this?

After this PR:

SET is memory-safe, atomic, and predictable.
GET returns stable, independent values.
No dangling pointers, no double frees, no overwriting internal state.

Instruments reports clean behavior for all tested GET/SET patterns.

Testing

  • Manual GET/SET/UPDATE sequences
  • Instruments profiling with intensive workload

Verified correct behavior on updates, overwrites, and missing keys

Next Steps (Optional Future PRs)

  • Add read-only zero-copy get_value_borrowed() for performance
  • Consider migrating hashtable to Robin Hood hashing
  • Add unit tests specifically targeting memory safety

This commit addresses several critical bugs in fkvs core hashtable
operations, significantly improving stability, correctness, and safety.

### Fixes in set_value():
- Prevent double-frees and incorrect cleanup paths by distinguishing
  properly between new and existing entries.
- Ensure atomic updates: old values are no longer freed until the new
  value is fully allocated, preventing value loss on partial failures.
- Correctly handle cleanup on any allocation failure without corrupting
  existing entries or leaving dangling bucket pointers.
- Support zero-length values safely.
- Remove invalid logic that attempted to detect “new entry” using the
  bucket head position, which caused corruption and crashes.

### Fixes in get_value():
- Allocate the correct size for value_entry_t instead of sizeof(pointer),
  which previously caused heap buffer overflows.
- Deep-copy value bytes into a new buffer instead of returning internal
  value pointers.
- Remove the use-after-free bug where out->ptr was freed before being
  returned to the caller.
- Ensure the returned value is stable, private, and not tied to internal
  storage.
- Add proper cleanup on allocation failures.

Together these changes eliminate multiple memory leaks, double frees,
dangling pointers, and use-after-free conditions. The hashtable read and
write paths are now correct, atomic, and safe for production workloads.
@AlexJuca AlexJuca self-assigned this Nov 14, 2025
@AlexJuca AlexJuca added bug Something isn't working enhancement New feature or request labels Nov 14, 2025
@AlexJuca AlexJuca merged commit bdb9ace into main Nov 14, 2025
2 checks passed
@AlexJuca AlexJuca deleted the fix-hashtable-ops branch November 15, 2025 13:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants