[PATCH BlueZ] shared/vcp: use iov_pull in input parsing

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Check input is right size by using iov_pull* when parsing.

Also replace custom iov_pull_mem by equivalent util_iov_pull_mem, and
add iov_pull_string.

Fixes handling of zero-length string-valued descriptors, !value is not
error.

Fixes crashes like:

ERROR: AddressSanitizer: stack-buffer-overflow
WRITE of size 3 at 0x7b878bb77161 thread T0
    #0 0x7f878eee4821 in memcpy
    #1 0x0000009544d4 in read_aics_aud_ip_type src/shared/vcp.c:2713
    #2 0x000000950cec in vcp_pending_complete src/shared/vcp.c:2394
    #3 0x00000088b2ce in read_cb src/shared/gatt-client.c:2717
---
 src/shared/vcp.c | 238 +++++++++++++++++++++++++----------------------
 1 file changed, 128 insertions(+), 110 deletions(-)

diff --git a/src/shared/vcp.c b/src/shared/vcp.c
index 321686774..c96ad4376 100644
--- a/src/shared/vcp.c
+++ b/src/shared/vcp.c
@@ -303,17 +303,24 @@ static struct queue *vcp_db;
 static struct queue *vcp_cbs;
 static struct queue *sessions;
 
-static void *iov_pull_mem(struct iovec *iov, size_t len)
+static char *iov_pull_string(struct iovec *iov)
 {
-	void *data = iov->iov_base;
+	char *res;
 
-	if (iov->iov_len < len)
+	if (!iov)
 		return NULL;
 
-	iov->iov_base += len;
-	iov->iov_len -= len;
+	res = malloc(iov->iov_len + 1);
+	if (!res)
+		return NULL;
 
-	return data;
+	if (iov->iov_len)
+		memcpy(res, iov->iov_base, iov->iov_len);
+
+	res[iov->iov_len] = 0;
+
+	util_iov_pull(iov, iov->iov_len);
+	return res;
 }
 
 static struct bt_vcp_db *vcp_get_vdb(struct bt_vcp *vcp)
@@ -577,7 +584,7 @@ static uint8_t vcs_rel_vol_down(struct bt_vcs *vcs, struct bt_vcp *vcp,
 		return 0;
 	}
 
-	change_counter = iov_pull_mem(iov, sizeof(*change_counter));
+	change_counter = util_iov_pull_mem(iov, sizeof(*change_counter));
 	if (!change_counter)
 		return 0;
 
@@ -620,7 +627,7 @@ static uint8_t vcs_rel_vol_up(struct bt_vcs *vcs, struct bt_vcp *vcp,
 		return 0;
 	}
 
-	change_counter = iov_pull_mem(iov, sizeof(*change_counter));
+	change_counter = util_iov_pull_mem(iov, sizeof(*change_counter));
 	if (!change_counter)
 		return 0;
 
@@ -663,7 +670,7 @@ static uint8_t vcs_unmute_rel_vol_down(struct bt_vcs *vcs, struct bt_vcp *vcp,
 		return 0;
 	}
 
-	change_counter = iov_pull_mem(iov, sizeof(*change_counter));
+	change_counter = util_iov_pull_mem(iov, sizeof(*change_counter));
 	if (!change_counter)
 		return 0;
 
@@ -707,7 +714,7 @@ static uint8_t vcs_unmute_rel_vol_up(struct bt_vcs *vcs, struct bt_vcp *vcp,
 		return 0;
 	}
 
-	change_counter = iov_pull_mem(iov, sizeof(*change_counter));
+	change_counter = util_iov_pull_mem(iov, sizeof(*change_counter));
 	if (!change_counter)
 		return 0;
 
@@ -751,7 +758,7 @@ static uint8_t vcs_set_absolute_vol(struct bt_vcs *vcs, struct bt_vcp *vcp,
 		return 0;
 	}
 
-	req = iov_pull_mem(iov, sizeof(*req));
+	req = util_iov_pull_mem(iov, sizeof(*req));
 	if (!req)
 		return 0;
 
@@ -794,7 +801,7 @@ static uint8_t vcs_unmute(struct bt_vcs *vcs, struct bt_vcp *vcp,
 		return 0;
 	}
 
-	change_counter = iov_pull_mem(iov, sizeof(*change_counter));
+	change_counter = util_iov_pull_mem(iov, sizeof(*change_counter));
 	if (!change_counter)
 		return 0;
 
@@ -833,7 +840,7 @@ static uint8_t vcs_mute(struct bt_vcs *vcs, struct bt_vcp *vcp,
 		return 0;
 	}
 
-	change_counter = iov_pull_mem(iov, sizeof(*change_counter));
+	change_counter = util_iov_pull_mem(iov, sizeof(*change_counter));
 	if (!change_counter)
 		return 0;
 
@@ -869,7 +876,7 @@ static uint8_t vocs_set_vol_offset(struct bt_vocs *vocs, struct bt_vcp *vcp,
 		return 0;
 	}
 
-	req = iov_pull_mem(iov, sizeof(*req));
+	req = util_iov_pull_mem(iov, sizeof(*req));
 	if (!req)
 		return 0;
 
@@ -992,9 +999,9 @@ static void vcs_cp_write(struct gatt_db_attribute *attrib,
 		goto respond;
 	}
 
-	vcp_op = iov_pull_mem(&iov, sizeof(*vcp_op));
+	vcp_op = util_iov_pull_mem(&iov, sizeof(*vcp_op));
 	if (!vcp_op) {
-		DBG(vcp, "iov_pull_mem() returned NULL");
+		DBG(vcp, "util_iov_pull_mem() returned NULL");
 		goto respond;
 	}
 
@@ -1056,9 +1063,9 @@ static void vocs_cp_write(struct gatt_db_attribute *attrib,
 		goto respond;
 	}
 
-	vcp_op = iov_pull_mem(&iov, sizeof(*vcp_op));
+	vcp_op = util_iov_pull_mem(&iov, sizeof(*vcp_op));
 	if (!vcp_op) {
-		DBG(vcp, "iov_pull_mem() returned NULL");
+		DBG(vcp, "util_iov_pull_mem() returned NULL");
 		goto respond;
 	}
 
@@ -1270,7 +1277,7 @@ static uint8_t aics_set_gain_setting(struct bt_aics *aics,
 
 	}
 
-	req = iov_pull_mem(iov, sizeof(*req));
+	req = util_iov_pull_mem(iov, sizeof(*req));
 	if (!req) {
 		ret = 0;
 		goto respond;
@@ -1333,7 +1340,7 @@ static uint8_t aics_unmute(struct bt_aics *aics, struct bt_vcp *vcp,
 		goto respond;
 
 	}
-	change_counter = iov_pull_mem(iov, sizeof(*change_counter));
+	change_counter = util_iov_pull_mem(iov, sizeof(*change_counter));
 	if (!change_counter) {
 		ret = 0;
 		goto respond;
@@ -1385,7 +1392,7 @@ static uint8_t aics_mute(struct bt_aics *aics, struct bt_vcp *vcp,
 		ret = 0;
 		goto respond;
 	}
-	change_counter = iov_pull_mem(iov, sizeof(*change_counter));
+	change_counter = util_iov_pull_mem(iov, sizeof(*change_counter));
 	if (!change_counter) {
 		ret = 0;
 		goto respond;
@@ -1437,7 +1444,7 @@ static uint8_t aics_set_manual_gain_mode(struct bt_aics *aics,
 		goto respond;
 	}
 
-	change_counter = iov_pull_mem(iov, sizeof(*change_counter));
+	change_counter = util_iov_pull_mem(iov, sizeof(*change_counter));
 	if (!change_counter) {
 		ret = 0;
 		goto respond;
@@ -1496,7 +1503,7 @@ static uint8_t aics_set_auto_gain_mode(struct bt_aics *aics, struct bt_vcp *vcp,
 		ret = 0;
 		goto respond;
 	}
-	change_counter = iov_pull_mem(iov, sizeof(*change_counter));
+	change_counter = util_iov_pull_mem(iov, sizeof(*change_counter));
 	if (!change_counter) {
 		ret = 0;
 		goto respond;
@@ -1592,9 +1599,9 @@ static void aics_ip_cp_write(struct gatt_db_attribute *attrib,
 		goto respond;
 	}
 
-	aics_op = iov_pull_mem(&iov, sizeof(*aics_op));
+	aics_op = util_iov_pull_mem(&iov, sizeof(*aics_op));
 	if (!aics_op) {
-		DBG(vcp, "iov_pull_mem() returned NULL");
+		DBG(vcp, "util_iov_pull_mem() returned NULL");
 		goto respond;
 	}
 
@@ -2045,16 +2052,21 @@ static void vcp_vstate_notify(struct bt_vcp *vcp, uint16_t value_handle,
 				const uint8_t *value, uint16_t length,
 				void *user_data)
 {
-	struct vol_state vstate;
+	struct iovec iov = { .iov_base = (void *)value, .iov_len = length };
+	struct vol_state *vstate;
 
-	memcpy(&vstate, value, sizeof(struct vol_state));
+	vstate = util_iov_pull_mem(&iov, sizeof(*vstate));
+	if (!vstate) {
+		DBG(vcp, "Invalid Vol State");
+		return;
+	}
 
-	DBG(vcp, "Vol Settings 0x%x", vstate.vol_set);
-	DBG(vcp, "Mute Status 0x%x", vstate.mute);
-	DBG(vcp, "Vol Counter 0x%x", vstate.counter);
+	DBG(vcp, "Vol Settings 0x%x", vstate->vol_set);
+	DBG(vcp, "Mute Status 0x%x", vstate->mute);
+	DBG(vcp, "Vol Counter 0x%x", vstate->counter);
 
-	vcp->volume = vstate.vol_set;
-	vcp->volume_counter = vstate.counter;
+	vcp->volume = vstate->vol_set;
+	vcp->volume_counter = vstate->counter;
 
 	if (vcp->volume_changed)
 		vcp->volume_changed(vcp, vcp->volume);
@@ -2190,27 +2202,32 @@ static void vcp_voffset_state_notify(struct bt_vcp *vcp, uint16_t value_handle,
 				const uint8_t *value, uint16_t length,
 				void *user_data)
 {
-	struct vol_offset_state vostate;
+	struct iovec iov = { .iov_base = (void *)value, .iov_len = length };
+	struct vol_offset_state *vostate;
 
-	memcpy(&vostate, value, sizeof(struct vol_offset_state));
+	vostate = util_iov_pull_mem(&iov, sizeof(*vostate));
+	if (!vostate) {
+		DBG(vcp, "Invalid Vol Offset State");
+		return;
+	}
 
-	DBG(vcp, "Vol Offset 0x%x", vostate.vol_offset);
-	DBG(vcp, "Vol Offset Counter 0x%x", vostate.counter);
+	DBG(vcp, "Vol Offset 0x%x", vostate->vol_offset);
+	DBG(vcp, "Vol Offset Counter 0x%x", vostate->counter);
 }
 
 static void vcp_audio_loc_notify(struct bt_vcp *vcp, uint16_t value_handle,
 				const uint8_t *value, uint16_t length,
 				void *user_data)
 {
-	uint32_t *vocs_audio_loc_n = malloc(sizeof(uint32_t));
-	*vocs_audio_loc_n = 0;
+	struct iovec iov = { .iov_base = (void *)value, .iov_len = length };
+	uint32_t audio_loc;
 
-	if (value != NULL)
-		memcpy(vocs_audio_loc_n, value, sizeof(uint32_t));
+	if (!util_iov_pull_le32(&iov, &audio_loc)) {
+		DBG(vcp, "Invalid VOCS Audio Location");
+		return;
+	}
 
-	DBG(vcp, "VOCS Audio Location 0x%x", *vocs_audio_loc_n);
-
-	free(vocs_audio_loc_n);
+	DBG(vcp, "VOCS Audio Location 0x%x", audio_loc);
 }
 
 
@@ -2220,20 +2237,29 @@ static void vcp_audio_descriptor_notify(struct bt_vcp *vcp,
 					uint16_t length,
 					void *user_data)
 {
-	char vocs_audio_dec_n[256] = {'\0'};
+	struct iovec iov = { .iov_base = (void *)value, .iov_len = length };
+	char *vocs_audio_dec;
 
-	memcpy(vocs_audio_dec_n, value, length);
+	vocs_audio_dec = iov_pull_string(&iov);
+	if (!vocs_audio_dec)
+		return;
 
-	DBG(vcp, "VOCS Audio Descriptor 0x%s", *vocs_audio_dec_n);
+	DBG(vcp, "VOCS Audio Descriptor 0x%s", vocs_audio_dec);
+
+	free(vocs_audio_dec);
 }
 
 static void vcp_vflag_notify(struct bt_vcp *vcp, uint16_t value_handle,
 			     const uint8_t *value, uint16_t length,
 			     void *user_data)
 {
+	struct iovec iov = { .iov_base = (void *)value, .iov_len = length };
 	uint8_t vflag;
 
-	memcpy(&vflag, value, sizeof(vflag));
+	if (!util_iov_pull_u8(&iov, &vflag)) {
+		DBG(vcp, "Invalid Vol Flag");
+		return;
+	}
 
 	DBG(vcp, "Vol Flag 0x%x", vflag);
 }
@@ -2242,24 +2268,20 @@ static void read_vol_flag(struct bt_vcp *vcp, bool success, uint8_t att_ecode,
 				const uint8_t *value, uint16_t length,
 				void *user_data)
 {
-	uint8_t *vol_flag;
-	struct iovec iov = {
-		.iov_base = (void *) value,
-		.iov_len = length,
-	};
+	struct iovec iov = { .iov_base = (void *) value, .iov_len = length };
+	uint8_t vol_flag;
 
 	if (!success) {
 		DBG(vcp, "Unable to read Vol Flag: error 0x%02x", att_ecode);
 		return;
 	}
 
-	vol_flag = iov_pull_mem(&iov, sizeof(*vol_flag));
-	if (!vol_flag) {
+	if (!util_iov_pull_u8(&iov, &vol_flag)) {
 		DBG(vcp, "Unable to get Vol Flag");
 		return;
 	}
 
-	DBG(vcp, "Vol Flag:%x", *vol_flag);
+	DBG(vcp, "Vol Flag:%x", vol_flag);
 }
 
 static void read_vol_state(struct bt_vcp *vcp, bool success, uint8_t att_ecode,
@@ -2277,7 +2299,7 @@ static void read_vol_state(struct bt_vcp *vcp, bool success, uint8_t att_ecode,
 		return;
 	}
 
-	vs = iov_pull_mem(&iov, sizeof(*vs));
+	vs = util_iov_pull_mem(&iov, sizeof(*vs));
 	if (!vs) {
 		DBG(vcp, "Unable to get Vol State");
 		return;
@@ -2308,7 +2330,7 @@ static void read_vol_offset_state(struct bt_vcp *vcp, bool success,
 		return;
 	}
 
-	vos = iov_pull_mem(&iov, sizeof(*vos));
+	vos = util_iov_pull_mem(&iov, sizeof(*vos));
 	if (!vos) {
 		DBG(vcp, "Unable to get Vol Offset State");
 		return;
@@ -2323,13 +2345,8 @@ static void read_vocs_audio_location(struct bt_vcp *vcp, bool success,
 				     const uint8_t *value, uint16_t length,
 				     void *user_data)
 {
+	struct iovec iov = { .iov_base = (void *)value, .iov_len = length };
 	uint32_t vocs_audio_loc;
-	struct iovec iov;
-
-	if (!value) {
-		DBG(vcp, "Unable to get VOCS Audio Location");
-		return;
-	}
 
 	if (!success) {
 		DBG(vcp, "Unable to read VOCS Audio Location: error 0x%02x",
@@ -2337,9 +2354,6 @@ static void read_vocs_audio_location(struct bt_vcp *vcp, bool success,
 		return;
 	}
 
-	iov.iov_base = (void *)value;
-	iov.iov_len = length;
-
 	if (!util_iov_pull_le32(&iov, &vocs_audio_loc)) {
 		DBG(vcp, "Invalid size for VOCS Audio Location");
 		return;
@@ -2354,12 +2368,8 @@ static void read_vocs_audio_descriptor(struct bt_vcp *vcp, bool success,
 				       const uint8_t *value, uint16_t length,
 				       void *user_data)
 {
-	char *vocs_ao_dec_r;
-
-	if (!value) {
-		DBG(vcp, "Unable to get VOCS Audio Location");
-		return;
-	}
+	struct iovec iov = { .iov_base = (void *)value, .iov_len = length };
+	char *vocs_ao_dec;
 
 	if (!success) {
 		DBG(vcp, "Unable to read VOCS Audio Descriptor: error 0x%02x",
@@ -2367,12 +2377,13 @@ static void read_vocs_audio_descriptor(struct bt_vcp *vcp, bool success,
 		return;
 	}
 
-	vocs_ao_dec_r = util_memdup(value, length + 1);
-	memset(vocs_ao_dec_r + length, 0, 1);
+	vocs_ao_dec = iov_pull_string(&iov);
+	if (!vocs_ao_dec)
+		return;
 
-	DBG(vcp, "VOCS Audio Descriptor: %s", vocs_ao_dec_r);
-	free(vocs_ao_dec_r);
-	vocs_ao_dec_r = NULL;
+	DBG(vcp, "VOCS Audio Descriptor: %s", vocs_ao_dec);
+
+	free(vocs_ao_dec);
 }
 
 static void vcp_pending_destroy(void *data)
@@ -2632,9 +2643,9 @@ static void read_aics_audio_ip_state(struct bt_vcp *vcp, bool success,
 		return;
 	}
 
-	ip_st = iov_pull_mem(&iov, sizeof(*ip_st));
+	ip_st = util_iov_pull_mem(&iov, sizeof(*ip_st));
 	if (!ip_st) {
-		DBG(vcp, "Unable to get Audio Input State");
+		DBG(vcp, "Invalid Audio Input State");
 		return;
 	}
 
@@ -2648,14 +2659,19 @@ static void aics_ip_state_notify(struct bt_vcp *vcp, uint16_t value_handle,
 				const uint8_t *value, uint16_t length,
 				void *user_data)
 {
-	struct aud_ip_st ip_st;
+	struct iovec iov = { .iov_base = (void *) value, .iov_len = length };
+	struct aud_ip_st *ip_st;
 
-	memcpy(&ip_st, value, sizeof(struct aud_ip_st));
+	ip_st = util_iov_pull_mem(&iov, sizeof(*ip_st));
+	if (!ip_st) {
+		DBG(vcp, "Invalid Audio Input State");
+		return;
+	}
 
-	DBG(vcp, "Audio Input State, Gain Setting:%d", ip_st.gain_setting);
-	DBG(vcp, "Audio Input State, Mute:%x", ip_st.mute);
-	DBG(vcp, "Audio Input State, Gain Mode:%x", ip_st.gain_mode);
-	DBG(vcp, "Audio Input State, Change Counter:%x", ip_st.chg_counter);
+	DBG(vcp, "Audio Input State, Gain Setting:%d", ip_st->gain_setting);
+	DBG(vcp, "Audio Input State, Mute:%x", ip_st->mute);
+	DBG(vcp, "Audio Input State, Gain Mode:%x", ip_st->gain_mode);
+	DBG(vcp, "Audio Input State, Change Counter:%x", ip_st->chg_counter);
 }
 
 static void read_aics_gain_setting_prop(struct bt_vcp *vcp, bool success,
@@ -2663,16 +2679,8 @@ static void read_aics_gain_setting_prop(struct bt_vcp *vcp, bool success,
 					 const uint8_t *value, uint16_t length,
 					 void *user_data)
 {
+	struct iovec iov = { .iov_base = (void *) value, .iov_len = length };
 	struct gain_setting_prop *aics_gain_setting_prop;
-	struct iovec iov = {
-		.iov_base = (void *) value,
-		.iov_len = length,
-	};
-
-	if (!value) {
-		DBG(vcp, "Unable to get Gain Setting Properties Char");
-		return;
-	}
 
 	if (!success) {
 		DBG(vcp,
@@ -2681,7 +2689,7 @@ static void read_aics_gain_setting_prop(struct bt_vcp *vcp, bool success,
 		return;
 	}
 
-	aics_gain_setting_prop = iov_pull_mem(&iov,
+	aics_gain_setting_prop = util_iov_pull_mem(&iov,
 				sizeof(*aics_gain_setting_prop));
 	if (!aics_gain_setting_prop) {
 		DBG(vcp, "Unable to get Gain Setting Properties Char");
@@ -2701,6 +2709,7 @@ static void read_aics_aud_ip_type(struct bt_vcp *vcp, bool success,
 					 const uint8_t *value, uint16_t length,
 					 void *user_data)
 {
+	struct iovec iov = { .iov_base = (void *) value, .iov_len = length };
 	uint8_t ip_type;
 
 	if (!success) {
@@ -2710,7 +2719,10 @@ static void read_aics_aud_ip_type(struct bt_vcp *vcp, bool success,
 		return;
 	}
 
-	memcpy(&ip_type, value, length);
+	if (!util_iov_pull_u8(&iov, &ip_type)) {
+		DBG(vcp, "Invalid Audio Input Type Char");
+		return;
+	}
 
 	DBG(vcp, "Audio Input Type : %x", ip_type);
 }
@@ -2720,6 +2732,7 @@ static void read_aics_audio_ip_status(struct bt_vcp *vcp, bool success,
 					 const uint8_t *value, uint16_t length,
 					 void *user_data)
 {
+	struct iovec iov = { .iov_base = (void *) value, .iov_len = length };
 	uint8_t ip_status;
 
 	if (!success) {
@@ -2728,7 +2741,10 @@ static void read_aics_audio_ip_status(struct bt_vcp *vcp, bool success,
 		return;
 	}
 
-	memcpy(&ip_status, value, length);
+	if (!util_iov_pull_u8(&iov, &ip_status)) {
+		DBG(vcp, "Invalid Audio Input Status Char");
+		return;
+	}
 
 	DBG(vcp, "Audio Input Status : %x", ip_status);
 }
@@ -2738,9 +2754,13 @@ static void aics_ip_status_notify(struct bt_vcp *vcp, uint16_t value_handle,
 				uint16_t length,
 				void *user_data)
 {
+	struct iovec iov = { .iov_base = (void *) value, .iov_len = length };
 	uint8_t	ip_status;
 
-	memcpy(&ip_status, value, length);
+	if (!util_iov_pull_u8(&iov, &ip_status)) {
+		DBG(vcp, "Invalid Audio Input Status Char");
+		return;
+	}
 
 	DBG(vcp, "Audio Input Status, %x", ip_status);
 }
@@ -2751,13 +2771,9 @@ static void read_aics_audio_ip_description(struct bt_vcp *vcp, bool success,
 					   uint16_t length,
 					   void *user_data)
 {
+	struct iovec iov = { .iov_base = (void *) value, .iov_len = length };
 	char *ip_descrptn;
 
-	if (!value) {
-		DBG(vcp, "Unable to get Audio Input Description");
-		return;
-	}
-
 	if (!success) {
 		DBG(vcp,
 			"Unable to read Audio Input Description Char: error 0x%02x",
@@ -2765,27 +2781,29 @@ static void read_aics_audio_ip_description(struct bt_vcp *vcp, bool success,
 		return;
 	}
 
-	ip_descrptn = util_memdup(value, length + 1);
-	memset(ip_descrptn + length, 0, 1);
+	ip_descrptn = iov_pull_string(&iov);
+	if (!ip_descrptn)
+		return;
 
 	DBG(vcp, "Audio Input Description: %s", ip_descrptn);
+
 	free(ip_descrptn);
-	ip_descrptn = NULL;
 }
 
 static void aics_audio_ip_desr_notify(struct bt_vcp *vcp, uint16_t value_handle,
 				const uint8_t *value, uint16_t length,
 				void *user_data)
 {
+	struct iovec iov = { .iov_base = (void *) value, .iov_len = length };
 	char *aud_ip_desr;
 
-	aud_ip_desr = malloc(length+1);
-	memset(aud_ip_desr, 0, length+1);
-	memcpy(aud_ip_desr, value, length);
+	aud_ip_desr = iov_pull_string(&iov);
+	if (!aud_ip_desr)
+		return;
 
 	DBG(vcp, "Audio Input Description Notify, %s", aud_ip_desr);
+
 	free(aud_ip_desr);
-	aud_ip_desr = NULL;
 }
 
 static void foreach_aics_char(struct gatt_db_attribute *attr, void *user_data)
-- 
2.49.0





[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux