Skip to content

Commit 25637cf

Browse files
committed
CCBC-1610: fix invalid memory access when patching collection id
It is important to renew whole packet when patching collection id after the command has been encoded. Otherwise the packet might corrupt memory on deallocation. Change-Id: I68118133222de6129cd1b64ee08f08347bd7fc23 Reviewed-on: https://review.couchbase.org/c/libcouchbase/+/197287 Reviewed-by: Brett Lawson <brett19@gmail.com> Tested-by: Build Bot <build@couchbase.com>
1 parent 759915b commit 25637cf

3 files changed

Lines changed: 22 additions & 17 deletions

File tree

src/mc/mcreq.c

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -247,10 +247,10 @@ void mcreq_reenqueue_packet(mc_PIPELINE *pipeline, mc_PACKET *packet)
247247
sllist_insert_sorted(reqs, &packet->slnode, pkt_tmo_compar);
248248
}
249249

250-
static void check_collection_id(mc_PIPELINE *pipeline, mc_PACKET *packet)
250+
static mc_PACKET *check_collection_id(mc_PIPELINE *pipeline, mc_PACKET *packet)
251251
{
252252
if ((packet->flags & MCREQ_F_NOCID) != 0) {
253-
return;
253+
return packet;
254254
}
255255

256256
// before adding packet to pipeline lets see if we need add or remove collection id prefix
@@ -267,7 +267,7 @@ static void check_collection_id(mc_PIPELINE *pipeline, mc_PACKET *packet)
267267
key_length = ntohs(request->request.keylen);
268268
}
269269
if (key_length == 0) {
270-
return;
270+
return packet;
271271
}
272272

273273
char *key = header_and_key + sizeof(*request) + request->request.extlen + flexible_extras_length;
@@ -285,7 +285,7 @@ static void check_collection_id(mc_PIPELINE *pipeline, mc_PACKET *packet)
285285
if (collection_id_length == 0) {
286286
// but collection id prefix was not encoded, we should assume default collection and prepend zero as
287287
// a collection identifier
288-
mcreq_set_cid(pipeline, packet, 0);
288+
packet = mcreq_set_cid(packet, 0);
289289
}
290290
break;
291291

@@ -322,14 +322,15 @@ static void check_collection_id(mc_PIPELINE *pipeline, mc_PACKET *packet)
322322
}
323323
break;
324324
}
325+
return packet;
325326
}
326327

327328
void mcreq_enqueue_packet(mc_PIPELINE *pipeline, mc_PACKET *packet)
328329
{
329330
nb_SPAN *vspan = &packet->u_value.single;
330331
sllist_append(&pipeline->requests, &packet->slnode);
331332

332-
check_collection_id(pipeline, packet);
333+
packet = check_collection_id(pipeline, packet);
333334
netbuf_enqueue_span(&pipeline->nbmgr, &packet->kh_span, packet);
334335
MC_INCR_METRIC(pipeline, bytes_queued, packet->kh_span.size);
335336

@@ -357,7 +358,7 @@ void mcreq_enqueue_packet(mc_PIPELINE *pipeline, mc_PACKET *packet)
357358
void mcreq_wipe_packet(mc_PIPELINE *pipeline, mc_PACKET *packet)
358359
{
359360
if (!(packet->flags & MCREQ_F_KEY_NOCOPY)) {
360-
if ((packet->flags & MCREQ_F_DETACHED) || IS_STANDALONE_SPAN(&packet->kh_span)) {
361+
if ((packet->flags & MCREQ_F_DETACHED)) {
361362
free(SPAN_BUFFER(&packet->kh_span));
362363
} else {
363364
netbuf_mblock_release(&pipeline->nbmgr, &packet->kh_span);
@@ -629,7 +630,7 @@ lcb_STATUS mcreq_basic_packet(mc_CMDQUEUE *queue, const lcb_KEYBUF *key, uint32_
629630
return LCB_SUCCESS;
630631
}
631632

632-
void mcreq_set_cid(mc_PIPELINE *pipeline, mc_PACKET *packet, uint32_t cid)
633+
static void mcreq_set_cid_field(mc_PACKET *packet, uint32_t cid)
633634
{
634635
nb_SPAN old_span = packet->kh_span;
635636

@@ -684,15 +685,19 @@ void mcreq_set_cid(mc_PIPELINE *pipeline, mc_PACKET *packet, uint32_t cid)
684685
memcpy(new_header_and_key + header_size + collection_id_length, ptr, key_length - old_collection_id_length);
685686

686687
// deallocate the old span
687-
if (IS_STANDALONE_SPAN(&old_span)) {
688-
/* standalone buffer */
689-
free(SPAN_BUFFER(&old_span));
690-
} else {
691-
netbuf_mblock_release(&pipeline->nbmgr, &old_span);
692-
}
688+
lcb_assert(IS_STANDALONE_SPAN(&old_span));
689+
free(SPAN_BUFFER(&old_span));
693690

694691
packet->flags |= MCREQ_F_HASCID;
695-
packet->flags &= ~MCREQ_F_KEY_NOCOPY;
692+
}
693+
694+
mc_PACKET *mcreq_set_cid(mc_PACKET *packet, uint32_t cid)
695+
{
696+
if ((packet->flags & MCREQ_F_DETACHED) == 0) {
697+
packet = mcreq_renew_packet(packet);
698+
}
699+
mcreq_set_cid_field(packet, cid);
700+
return packet;
696701
}
697702

698703
uint32_t mcreq_get_cid(lcb_INSTANCE *instance, const mc_PACKET *packet, int *cid_set)

src/mc/mcreq.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -705,7 +705,7 @@ uint32_t mcreq_get_size(const mc_PACKET *packet);
705705

706706
uint32_t mcreq_get_cid(lcb_INSTANCE *instance, const mc_PACKET *packet, int *cid_set);
707707

708-
void mcreq_set_cid(mc_PIPELINE *pipeline, mc_PACKET *packet, uint32_t cid);
708+
mc_PACKET *mcreq_set_cid(mc_PACKET *packet, uint32_t cid);
709709

710710
/**
711711
* @brief Get the vBucket for the request

src/mcserver/mcserver.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -299,9 +299,9 @@ bool Server::handle_unknown_collection(MemcachedResponse &resp, mc_PACKET *oldpk
299299
wrapper.pkt = mcreq_renew_packet(oldpkt);
300300
wrapper.instance = instance;
301301
wrapper.timeout = LCB_NS2US(MCREQ_PKT_RDATA(wrapper.pkt)->deadline - now);
302-
auto operation = [this, orig_status](const lcb_RESPGETCID *, packet_wrapper *wrp) {
302+
auto operation = [orig_status](const lcb_RESPGETCID *, packet_wrapper *wrp) {
303303
if ((wrp->pkt->flags & MCREQ_F_NOCID) == 0) {
304-
mcreq_set_cid(this, wrp->pkt, wrp->cid);
304+
wrp->pkt = mcreq_set_cid(wrp->pkt, wrp->cid);
305305
}
306306
/** Reschedule the packet again .. */
307307
wrp->pkt->flags &= ~MCREQ_STATE_FLAGS;

0 commit comments

Comments
 (0)