Blame SOURCES/0001-sdpd-Fix-leaking-buffers-stored-in-cstates-cache.patch

dfd1bb
From 4e6a2402ed4f46ea026ad0929fbc14faecf3a475 Mon Sep 17 00:00:00 2001
dfd1bb
From: Gopal Tiwari <gtiwari@redhat.com>
dfd1bb
Date: Wed, 1 Dec 2021 12:18:24 +0530
dfd1bb
Subject: [PATCH BlueZ] sdpd: Fix leaking buffers stored in cstates cache
dfd1bb
dfd1bb
commit e79417ed7185b150a056d4eb3a1ab528b91d2fc0
dfd1bb
Author: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
dfd1bb
Date:   Thu Jul 15 11:01:20 2021 -0700
dfd1bb
dfd1bb
    sdpd: Fix leaking buffers stored in cstates cache
dfd1bb
dfd1bb
    These buffer shall only be keep in cache for as long as they are
dfd1bb
    needed so this would cleanup any client cstates in the following
dfd1bb
    conditions:
dfd1bb
dfd1bb
     - There is no cstate on the response
dfd1bb
     - No continuation can be found for cstate
dfd1bb
     - Different request opcode
dfd1bb
     - Respond with an error
dfd1bb
     - Client disconnect
dfd1bb
dfd1bb
    Fixes: https://github.com/bluez/bluez/security/advisories/GHSA-3fqg-r8j5-f5xq
dfd1bb
---
dfd1bb
 src/sdpd-request.c | 170 ++++++++++++++++++++++++++++++++-------------
dfd1bb
 src/sdpd-server.c  |  20 +++---
dfd1bb
 src/sdpd.h         |   3 +
dfd1bb
 unit/test-sdp.c    |   2 +-
dfd1bb
 4 files changed, 135 insertions(+), 60 deletions(-)
dfd1bb
dfd1bb
diff --git a/src/sdpd-request.c b/src/sdpd-request.c
dfd1bb
index 033d1e5bf..c8f5a2c72 100644
dfd1bb
--- a/src/sdpd-request.c
dfd1bb
+++ b/src/sdpd-request.c
dfd1bb
@@ -42,48 +42,78 @@ typedef struct {
dfd1bb
 
dfd1bb
 #define MIN(x, y) ((x) < (y)) ? (x): (y)
dfd1bb
 
dfd1bb
-typedef struct _sdp_cstate_list sdp_cstate_list_t;
dfd1bb
+typedef struct sdp_cont_info sdp_cont_info_t;
dfd1bb
 
dfd1bb
-struct _sdp_cstate_list {
dfd1bb
-	sdp_cstate_list_t *next;
dfd1bb
+struct sdp_cont_info {
dfd1bb
+	int sock;
dfd1bb
+	uint8_t opcode;
dfd1bb
 	uint32_t timestamp;
dfd1bb
 	sdp_buf_t buf;
dfd1bb
 };
dfd1bb
 
dfd1bb
-static sdp_cstate_list_t *cstates;
dfd1bb
+static sdp_list_t *cstates;
dfd1bb
 
dfd1bb
-/* FIXME: should probably remove it when it's found */
dfd1bb
-static sdp_buf_t *sdp_get_cached_rsp(sdp_cont_state_t *cstate)
dfd1bb
+static int cstate_match(const void *data, const void *user_data)
dfd1bb
 {
dfd1bb
-	sdp_cstate_list_t *p;
dfd1bb
+	const sdp_cont_info_t *cinfo = data;
dfd1bb
+	const sdp_cont_state_t *cstate = user_data;
dfd1bb
 
dfd1bb
-	for (p = cstates; p; p = p->next) {
dfd1bb
-		/* Check timestamp */
dfd1bb
-		if (p->timestamp != cstate->timestamp)
dfd1bb
-			continue;
dfd1bb
+	/* Check timestamp */
dfd1bb
+	return cinfo->timestamp - cstate->timestamp;
dfd1bb
+}
dfd1bb
+
dfd1bb
+static void sdp_cont_info_free(sdp_cont_info_t *cinfo)
dfd1bb
+{
dfd1bb
+	if (!cinfo)
dfd1bb
+		return;
dfd1bb
+
dfd1bb
+	cstates = sdp_list_remove(cstates, cinfo);
dfd1bb
+	free(cinfo->buf.data);
dfd1bb
+	free(cinfo);
dfd1bb
+}
dfd1bb
+
dfd1bb
+static sdp_cont_info_t *sdp_get_cont_info(sdp_req_t *req,
dfd1bb
+						sdp_cont_state_t *cstate)
dfd1bb
+{
dfd1bb
+	sdp_list_t *list;
dfd1bb
+
dfd1bb
+	list = sdp_list_find(cstates, cstate, cstate_match);
dfd1bb
+	if (list) {
dfd1bb
+		sdp_cont_info_t *cinfo = list->data;
dfd1bb
 
dfd1bb
-		/* Check if requesting more than available */
dfd1bb
-		if (cstate->cStateValue.maxBytesSent < p->buf.data_size)
dfd1bb
-			return &p->buf;
dfd1bb
+		if (cinfo->opcode == req->opcode)
dfd1bb
+			return cinfo;
dfd1bb
+
dfd1bb
+		/* Cleanup continuation if the opcode doesn't match since its
dfd1bb
+		 * response buffer shall only be valid for the original requests
dfd1bb
+		 */
dfd1bb
+		sdp_cont_info_free(cinfo);
dfd1bb
+		return NULL;
dfd1bb
 	}
dfd1bb
 
dfd1bb
-	return 0;
dfd1bb
+	/* Cleanup cstates if no continuation info could be found */
dfd1bb
+	sdp_cstate_cleanup(req->sock);
dfd1bb
+
dfd1bb
+	return NULL;
dfd1bb
 }
dfd1bb
 
dfd1bb
-static uint32_t sdp_cstate_alloc_buf(sdp_buf_t *buf)
dfd1bb
+static uint32_t sdp_cstate_alloc_buf(sdp_req_t *req, sdp_buf_t *buf)
dfd1bb
 {
dfd1bb
-	sdp_cstate_list_t *cstate = malloc(sizeof(sdp_cstate_list_t));
dfd1bb
+	sdp_cont_info_t *cinfo = malloc(sizeof(sdp_cont_info_t));
dfd1bb
 	uint8_t *data = malloc(buf->data_size);
dfd1bb
 
dfd1bb
 	memcpy(data, buf->data, buf->data_size);
dfd1bb
-	memset((char *)cstate, 0, sizeof(sdp_cstate_list_t));
dfd1bb
-	cstate->buf.data = data;
dfd1bb
-	cstate->buf.data_size = buf->data_size;
dfd1bb
-	cstate->buf.buf_size = buf->data_size;
dfd1bb
-	cstate->timestamp = sdp_get_time();
dfd1bb
-	cstate->next = cstates;
dfd1bb
-	cstates = cstate;
dfd1bb
-	return cstate->timestamp;
dfd1bb
+	memset(cinfo, 0, sizeof(sdp_cont_info_t));
dfd1bb
+	cinfo->buf.data = data;
dfd1bb
+	cinfo->buf.data_size = buf->data_size;
dfd1bb
+	cinfo->buf.buf_size = buf->data_size;
dfd1bb
+	cinfo->timestamp = sdp_get_time();
dfd1bb
+	cinfo->sock = req->sock;
dfd1bb
+	cinfo->opcode = req->opcode;
dfd1bb
+
dfd1bb
+	cstates = sdp_list_append(cstates, cinfo);
dfd1bb
+
dfd1bb
+	return cinfo->timestamp;
dfd1bb
 }
dfd1bb
 
dfd1bb
 /* Additional values for checking datatype (not in spec) */
dfd1bb
@@ -274,14 +304,16 @@ static int sdp_set_cstate_pdu(sdp_buf_t *buf, sdp_cont_state_t *cstate)
dfd1bb
 	return length;
dfd1bb
 }
dfd1bb
 
dfd1bb
-static int sdp_cstate_get(uint8_t *buffer, size_t len,
dfd1bb
-						sdp_cont_state_t **cstate)
dfd1bb
+static int sdp_cstate_get(sdp_req_t *req, uint8_t *buffer, size_t len,
dfd1bb
+			sdp_cont_state_t **cstate, sdp_cont_info_t **cinfo)
dfd1bb
 {
dfd1bb
 	uint8_t cStateSize = *buffer;
dfd1bb
 
dfd1bb
 	SDPDBG("Continuation State size : %d", cStateSize);
dfd1bb
 
dfd1bb
 	if (cStateSize == 0) {
dfd1bb
+		/* Cleanup cstates if request doesn't contain a cstate */
dfd1bb
+		sdp_cstate_cleanup(req->sock);
dfd1bb
 		*cstate = NULL;
dfd1bb
 		return 0;
dfd1bb
 	}
dfd1bb
@@ -306,6 +338,8 @@ static int sdp_cstate_get(uint8_t *buffer, size_t len,
dfd1bb
 	SDPDBG("Cstate TS : 0x%x", (*cstate)->timestamp);
dfd1bb
 	SDPDBG("Bytes sent : %d", (*cstate)->cStateValue.maxBytesSent);
dfd1bb
 
dfd1bb
+	*cinfo = sdp_get_cont_info(req, *cstate);
dfd1bb
+
dfd1bb
 	return 0;
dfd1bb
 }
dfd1bb
 
dfd1bb
@@ -360,6 +394,7 @@ static int service_search_req(sdp_req_t *req, sdp_buf_t *buf)
dfd1bb
 	uint16_t expected, actual, rsp_count = 0;
dfd1bb
 	uint8_t dtd;
dfd1bb
 	sdp_cont_state_t *cstate = NULL;
dfd1bb
+	sdp_cont_info_t *cinfo = NULL;
dfd1bb
 	uint8_t *pCacheBuffer = NULL;
dfd1bb
 	int handleSize = 0;
dfd1bb
 	uint32_t cStateId = 0;
dfd1bb
@@ -399,9 +434,9 @@ static int service_search_req(sdp_req_t *req, sdp_buf_t *buf)
dfd1bb
 
dfd1bb
 	/*
dfd1bb
 	 * Check if continuation state exists, if yes attempt
dfd1bb
-	 * to get rsp remainder from cache, else send error
dfd1bb
+	 * to get rsp remainder from continuation info, else send error
dfd1bb
 	 */
dfd1bb
-	if (sdp_cstate_get(pdata, data_left, &cstate) < 0) {
dfd1bb
+	if (sdp_cstate_get(req, pdata, data_left, &cstate, &cinfo) < 0) {
dfd1bb
 		status = SDP_INVALID_SYNTAX;
dfd1bb
 		goto done;
dfd1bb
 	}
dfd1bb
@@ -451,7 +486,7 @@ static int service_search_req(sdp_req_t *req, sdp_buf_t *buf)
dfd1bb
 
dfd1bb
 		if (rsp_count > actual) {
dfd1bb
 			/* cache the rsp and generate a continuation state */
dfd1bb
-			cStateId = sdp_cstate_alloc_buf(buf);
dfd1bb
+			cStateId = sdp_cstate_alloc_buf(req, buf);
dfd1bb
 			/*
dfd1bb
 			 * subtract handleSize since we now send only
dfd1bb
 			 * a subset of handles
dfd1bb
@@ -459,6 +494,7 @@ static int service_search_req(sdp_req_t *req, sdp_buf_t *buf)
dfd1bb
 			buf->data_size -= handleSize;
dfd1bb
 		} else {
dfd1bb
 			/* NULL continuation state */
dfd1bb
+			sdp_cont_info_free(cinfo);
dfd1bb
 			sdp_set_cstate_pdu(buf, NULL);
dfd1bb
 		}
dfd1bb
 	}
dfd1bb
@@ -468,13 +504,15 @@ static int service_search_req(sdp_req_t *req, sdp_buf_t *buf)
dfd1bb
 		short lastIndex = 0;
dfd1bb
 
dfd1bb
 		if (cstate) {
dfd1bb
-			/*
dfd1bb
-			 * Get the previous sdp_cont_state_t and obtain
dfd1bb
-			 * the cached rsp
dfd1bb
-			 */
dfd1bb
-			sdp_buf_t *pCache = sdp_get_cached_rsp(cstate);
dfd1bb
-			if (pCache) {
dfd1bb
-				pCacheBuffer = pCache->data;
dfd1bb
+			if (cinfo) {
dfd1bb
+				/* Check if requesting more than available */
dfd1bb
+				if (cstate->cStateValue.maxBytesSent >=
dfd1bb
+						cinfo->buf.data_size) {
dfd1bb
+					status = SDP_INVALID_CSTATE;
dfd1bb
+					goto done;
dfd1bb
+				}
dfd1bb
+
dfd1bb
+				pCacheBuffer = cinfo->buf.data;
dfd1bb
 				/* get the rsp_count from the cached buffer */
dfd1bb
 				rsp_count = get_be16(pCacheBuffer);
dfd1bb
 
dfd1bb
@@ -518,6 +556,7 @@ static int service_search_req(sdp_req_t *req, sdp_buf_t *buf)
dfd1bb
 		if (i == rsp_count) {
dfd1bb
 			/* set "null" continuationState */
dfd1bb
 			sdp_set_cstate_pdu(buf, NULL);
dfd1bb
+			sdp_cont_info_free(cinfo);
dfd1bb
 		} else {
dfd1bb
 			/*
dfd1bb
 			 * there's more: set lastIndexSent to
dfd1bb
@@ -540,6 +579,7 @@ static int service_search_req(sdp_req_t *req, sdp_buf_t *buf)
dfd1bb
 
dfd1bb
 done:
dfd1bb
 	free(cstate);
dfd1bb
+
dfd1bb
 	if (pattern)
dfd1bb
 		sdp_list_free(pattern, free);
dfd1bb
 
dfd1bb
@@ -619,15 +659,21 @@ static int extract_attrs(sdp_record_t *rec, sdp_list_t *seq, sdp_buf_t *buf)
dfd1bb
 }
dfd1bb
 
dfd1bb
 /* Build cstate response */
dfd1bb
-static int sdp_cstate_rsp(sdp_cont_state_t *cstate, sdp_buf_t *buf,
dfd1bb
-							uint16_t max)
dfd1bb
+static int sdp_cstate_rsp(sdp_cont_info_t *cinfo, sdp_cont_state_t *cstate,
dfd1bb
+					sdp_buf_t *buf, uint16_t max)
dfd1bb
 {
dfd1bb
-	/* continuation State exists -> get from cache */
dfd1bb
-	sdp_buf_t *cache = sdp_get_cached_rsp(cstate);
dfd1bb
+	sdp_buf_t *cache;
dfd1bb
 	uint16_t sent;
dfd1bb
 
dfd1bb
-	if (!cache)
dfd1bb
+	if (!cinfo)
dfd1bb
+		return 0;
dfd1bb
+
dfd1bb
+	if (cstate->cStateValue.maxBytesSent >= cinfo->buf.data_size) {
dfd1bb
+		sdp_cont_info_free(cinfo);
dfd1bb
 		return 0;
dfd1bb
+	}
dfd1bb
+
dfd1bb
+	cache = &cinfo->buf;
dfd1bb
 
dfd1bb
 	sent = MIN(max, cache->data_size - cstate->cStateValue.maxBytesSent);
dfd1bb
 	memcpy(buf->data, cache->data + cstate->cStateValue.maxBytesSent, sent);
dfd1bb
@@ -637,8 +683,10 @@ static int sdp_cstate_rsp(sdp_cont_state_t *cstate, sdp_buf_t *buf,
dfd1bb
 	SDPDBG("Response size : %d sending now : %d bytes sent so far : %d",
dfd1bb
 		cache->data_size, sent, cstate->cStateValue.maxBytesSent);
dfd1bb
 
dfd1bb
-	if (cstate->cStateValue.maxBytesSent == cache->data_size)
dfd1bb
+	if (cstate->cStateValue.maxBytesSent == cache->data_size) {
dfd1bb
+		sdp_cont_info_free(cinfo);
dfd1bb
 		return sdp_set_cstate_pdu(buf, NULL);
dfd1bb
+	}
dfd1bb
 
dfd1bb
 	return sdp_set_cstate_pdu(buf, cstate);
dfd1bb
 }
dfd1bb
@@ -652,6 +700,7 @@ static int sdp_cstate_rsp(sdp_cont_state_t *cstate, sdp_buf_t *buf,
dfd1bb
 static int service_attr_req(sdp_req_t *req, sdp_buf_t *buf)
dfd1bb
 {
dfd1bb
 	sdp_cont_state_t *cstate = NULL;
dfd1bb
+	sdp_cont_info_t *cinfo = NULL;
dfd1bb
 	short cstate_size = 0;
dfd1bb
 	sdp_list_t *seq = NULL;
dfd1bb
 	uint8_t dtd = 0;
dfd1bb
@@ -708,7 +757,7 @@ static int service_attr_req(sdp_req_t *req, sdp_buf_t *buf)
dfd1bb
 	 * if continuation state exists, attempt
dfd1bb
 	 * to get rsp remainder from cache, else send error
dfd1bb
 	 */
dfd1bb
-	if (sdp_cstate_get(pdata, data_left, &cstate) < 0) {
dfd1bb
+	if (sdp_cstate_get(req, pdata, data_left, &cstate, &cinfo) < 0) {
dfd1bb
 		status = SDP_INVALID_SYNTAX;
dfd1bb
 		goto done;
dfd1bb
 	}
dfd1bb
@@ -737,7 +786,7 @@ static int service_attr_req(sdp_req_t *req, sdp_buf_t *buf)
dfd1bb
 	buf->buf_size -= sizeof(uint16_t);
dfd1bb
 
dfd1bb
 	if (cstate) {
dfd1bb
-		cstate_size = sdp_cstate_rsp(cstate, buf, max_rsp_size);
dfd1bb
+		cstate_size = sdp_cstate_rsp(cinfo, cstate, buf, max_rsp_size);
dfd1bb
 		if (!cstate_size) {
dfd1bb
 			status = SDP_INVALID_CSTATE;
dfd1bb
 			error("NULL cache buffer and non-NULL continuation state");
dfd1bb
@@ -749,7 +798,7 @@ static int service_attr_req(sdp_req_t *req, sdp_buf_t *buf)
dfd1bb
 			sdp_cont_state_t newState;
dfd1bb
 
dfd1bb
 			memset((char *)&newState, 0, sizeof(sdp_cont_state_t));
dfd1bb
-			newState.timestamp = sdp_cstate_alloc_buf(buf);
dfd1bb
+			newState.timestamp = sdp_cstate_alloc_buf(req, buf);
dfd1bb
 			/*
dfd1bb
 			 * Reset the buffer size to the maximum expected and
dfd1bb
 			 * set the sdp_cont_state_t
dfd1bb
@@ -793,6 +842,7 @@ static int service_search_attr_req(sdp_req_t *req, sdp_buf_t *buf)
dfd1bb
 	int scanned, rsp_count = 0;
dfd1bb
 	sdp_list_t *pattern = NULL, *seq = NULL, *svcList;
dfd1bb
 	sdp_cont_state_t *cstate = NULL;
dfd1bb
+	sdp_cont_info_t *cinfo = NULL;
dfd1bb
 	short cstate_size = 0;
dfd1bb
 	uint8_t dtd = 0;
dfd1bb
 	sdp_buf_t tmpbuf;
dfd1bb
@@ -852,7 +902,7 @@ static int service_search_attr_req(sdp_req_t *req, sdp_buf_t *buf)
dfd1bb
 	 * if continuation state exists attempt
dfd1bb
 	 * to get rsp remainder from cache, else send error
dfd1bb
 	 */
dfd1bb
-	if (sdp_cstate_get(pdata, data_left, &cstate) < 0) {
dfd1bb
+	if (sdp_cstate_get(req, pdata, data_left, &cstate, &cinfo) < 0) {
dfd1bb
 		status = SDP_INVALID_SYNTAX;
dfd1bb
 		goto done;
dfd1bb
 	}
dfd1bb
@@ -906,7 +956,7 @@ static int service_search_attr_req(sdp_req_t *req, sdp_buf_t *buf)
dfd1bb
 			sdp_cont_state_t newState;
dfd1bb
 
dfd1bb
 			memset((char *)&newState, 0, sizeof(sdp_cont_state_t));
dfd1bb
-			newState.timestamp = sdp_cstate_alloc_buf(buf);
dfd1bb
+			newState.timestamp = sdp_cstate_alloc_buf(req, buf);
dfd1bb
 			/*
dfd1bb
 			 * Reset the buffer size to the maximum expected and
dfd1bb
 			 * set the sdp_cont_state_t
dfd1bb
@@ -917,7 +967,7 @@ static int service_search_attr_req(sdp_req_t *req, sdp_buf_t *buf)
dfd1bb
 		} else
dfd1bb
 			cstate_size = sdp_set_cstate_pdu(buf, NULL);
dfd1bb
 	} else {
dfd1bb
-		cstate_size = sdp_cstate_rsp(cstate, buf, max);
dfd1bb
+		cstate_size = sdp_cstate_rsp(cinfo, cstate, buf, max);
dfd1bb
 		if (!cstate_size) {
dfd1bb
 			status = SDP_INVALID_CSTATE;
dfd1bb
 			SDPDBG("Non-null continuation state, but null cache buffer");
dfd1bb
@@ -974,6 +1024,9 @@ static void process_request(sdp_req_t *req)
dfd1bb
 		status = SDP_INVALID_PDU_SIZE;
dfd1bb
 		goto send_rsp;
dfd1bb
 	}
dfd1bb
+
dfd1bb
+	req->opcode = reqhdr->pdu_id;
dfd1bb
+
dfd1bb
 	switch (reqhdr->pdu_id) {
dfd1bb
 	case SDP_SVC_SEARCH_REQ:
dfd1bb
 		SDPDBG("Got a svc srch req");
dfd1bb
@@ -1020,6 +1073,8 @@ static void process_request(sdp_req_t *req)
dfd1bb
 
dfd1bb
 send_rsp:
dfd1bb
 	if (status) {
dfd1bb
+		/* Cleanup cstates on error */
dfd1bb
+		sdp_cstate_cleanup(req->sock);
dfd1bb
 		rsphdr->pdu_id = SDP_ERROR_RSP;
dfd1bb
 		put_be16(status, rsp.data);
dfd1bb
 		rsp.data_size = sizeof(uint16_t);
dfd1bb
@@ -1108,3 +1163,20 @@ void handle_request(int sk, uint8_t *data, int len)
dfd1bb
 
dfd1bb
 	process_request(&req;;
dfd1bb
 }
dfd1bb
+
dfd1bb
+void sdp_cstate_cleanup(int sock)
dfd1bb
+{
dfd1bb
+	sdp_list_t *list;
dfd1bb
+
dfd1bb
+	/* Remove any cinfo for the client */
dfd1bb
+	for (list = cstates; list;) {
dfd1bb
+		sdp_cont_info_t *cinfo = list->data;
dfd1bb
+
dfd1bb
+		list = list->next;
dfd1bb
+
dfd1bb
+		if (cinfo->sock != sock)
dfd1bb
+			continue;
dfd1bb
+
dfd1bb
+		sdp_cont_info_free(cinfo);
dfd1bb
+	}
dfd1bb
+}
dfd1bb
diff --git a/src/sdpd-server.c b/src/sdpd-server.c
dfd1bb
index dfd8b1f00..66ee7ba14 100644
dfd1bb
--- a/src/sdpd-server.c
dfd1bb
+++ b/src/sdpd-server.c
dfd1bb
@@ -146,16 +146,12 @@ static gboolean io_session_event(GIOChannel *chan, GIOCondition cond, gpointer d
dfd1bb
 
dfd1bb
 	sk = g_io_channel_unix_get_fd(chan);
dfd1bb
 
dfd1bb
-	if (cond & (G_IO_HUP | G_IO_ERR)) {
dfd1bb
-		sdp_svcdb_collect_all(sk);
dfd1bb
-		return FALSE;
dfd1bb
-	}
dfd1bb
+	if (cond & (G_IO_HUP | G_IO_ERR))
dfd1bb
+		goto cleanup;
dfd1bb
 
dfd1bb
 	len = recv(sk, &hdr, sizeof(sdp_pdu_hdr_t), MSG_PEEK);
dfd1bb
-	if (len < 0 || (unsigned int) len < sizeof(sdp_pdu_hdr_t)) {
dfd1bb
-		sdp_svcdb_collect_all(sk);
dfd1bb
-		return FALSE;
dfd1bb
-	}
dfd1bb
+	if (len < 0 || (unsigned int) len < sizeof(sdp_pdu_hdr_t))
dfd1bb
+		goto cleanup;
dfd1bb
 
dfd1bb
 	size = sizeof(sdp_pdu_hdr_t) + ntohs(hdr.plen);
dfd1bb
 	buf = malloc(size);
dfd1bb
@@ -168,14 +164,18 @@ static gboolean io_session_event(GIOChannel *chan, GIOCondition cond, gpointer d
dfd1bb
 	 * inside handle_request() in order to produce ErrorResponse.
dfd1bb
 	 */
dfd1bb
 	if (len <= 0) {
dfd1bb
-		sdp_svcdb_collect_all(sk);
dfd1bb
 		free(buf);
dfd1bb
-		return FALSE;
dfd1bb
+		goto cleanup;
dfd1bb
 	}
dfd1bb
 
dfd1bb
 	handle_request(sk, buf, len);
dfd1bb
 
dfd1bb
 	return TRUE;
dfd1bb
+
dfd1bb
+cleanup:
dfd1bb
+	sdp_svcdb_collect_all(sk);
dfd1bb
+	sdp_cstate_cleanup(sk);
dfd1bb
+	return FALSE;
dfd1bb
 }
dfd1bb
 
dfd1bb
 static gboolean io_accept_event(GIOChannel *chan, GIOCondition cond, gpointer data)
dfd1bb
diff --git a/src/sdpd.h b/src/sdpd.h
dfd1bb
index 257411f03..4316aff67 100644
dfd1bb
--- a/src/sdpd.h
dfd1bb
+++ b/src/sdpd.h
dfd1bb
@@ -27,8 +27,11 @@ typedef struct request {
dfd1bb
 	int      flags;
dfd1bb
 	uint8_t  *buf;
dfd1bb
 	int      len;
dfd1bb
+	uint8_t  opcode;
dfd1bb
 } sdp_req_t;
dfd1bb
 
dfd1bb
+void sdp_cstate_cleanup(int sock);
dfd1bb
+
dfd1bb
 void handle_internal_request(int sk, int mtu, void *data, int len);
dfd1bb
 void handle_request(int sk, uint8_t *data, int len);
dfd1bb
 
dfd1bb
diff --git a/unit/test-sdp.c b/unit/test-sdp.c
dfd1bb
index d3a885f19..8f95fcb71 100644
dfd1bb
--- a/unit/test-sdp.c
dfd1bb
+++ b/unit/test-sdp.c
dfd1bb
@@ -235,7 +235,7 @@ static gboolean client_handler(GIOChannel *channel, GIOCondition cond,
dfd1bb
 	tester_monitor('>', 0x0000, 0x0001, buf, len);
dfd1bb
 
dfd1bb
 	g_assert(len > 0);
dfd1bb
-	g_assert((size_t) len == rsp_pdu->raw_size + rsp_pdu->cont_len);
dfd1bb
+	g_assert_cmpuint(len, ==, rsp_pdu->raw_size + rsp_pdu->cont_len);
dfd1bb
 
dfd1bb
 	g_assert(memcmp(buf, rsp_pdu->raw_data,	rsp_pdu->raw_size) == 0);
dfd1bb
 
dfd1bb
-- 
dfd1bb
2.26.2
dfd1bb