Skip to content

Potential double-free in ramalhete_queue node destructor under high contention #44

@yehsi2000

Description

@yehsi2000

I am developing a lock-free IOCP game server using ramalhete_queue to manage std::unique_ptr which is implemented like this.

struct RecvPacket {
  clientid_t senderClientId;
  PacketPtr packet;

  RecvPacket() = default;
  ~RecvPacket() = default;

  RecvPacket(const RecvPacket&) = delete;
  RecvPacket& operator=(const RecvPacket&) = delete;

  RecvPacket(RecvPacket&&) = default;
  RecvPacket& operator=(RecvPacket&&) = default;
};

Under high push contention, I've encountered a double-free error. The issue appears to be in the queue's node destructor:

~node() override {
  for (unsigned i = pop_idx; i < push_idx; i += step_size) {
    traits::delete_value(entries[i % entries_per_node].value.load(std::memory_order_relaxed).get());
  }
}

A race condition can lead to a state where pop_idx and push_idx have wrapped around, and push_idx > pop_idx.
In this case, i % entries_per_node indexed an entry that has already been popped and its unique_ptr moved. The destructor then calls delete_value on the stale pointer, causing a double-free.

I was able to fix this by modifying the pop logic to atomically nullify the entry upon retrieval using exchange

if (value != nullptr) {
  // (14) - this acquire-load synchronizes-with the release-CAS (8)
  // std::ignore = h->entries[idx].value.load(std::memory_order_acquire); //original code
  std::ignore = h->entries[idx].value.exchange(marked_value(nullptr, 1), std::memory_order_acquire); // my fix
  return traits::get(value.get());
}

With this change, the double-free error is completely gone, even under heavy load.

While I'm not an expert in lock-free programming, I think this might be potential vulnerability in the current implementation I'm submitting this issue for your consideration, as it might be a genuine bug.
Thanks.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions