Skip to content

Commit 35a76aa

Browse files
olivier-le-sageeriksandgren
authored andcommitted
bluetooth: host: Unit tests for bt_le_cs_step_data_parse
Add unit tests for bt_le_cs_step_data_parse. Also exit early (without calling the function pointer) if the next step would seem to read out of bounds. Signed-off-by: Olivier Lesage <[email protected]>
1 parent e49e65b commit 35a76aa

File tree

12 files changed

+367
-0
lines changed

12 files changed

+367
-0
lines changed

subsys/bluetooth/host/cs.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,6 +1313,11 @@ void bt_le_cs_step_data_parse(struct net_buf_simple *step_data_buf,
13131313

13141314
step.data = step_data_buf->data;
13151315

1316+
if (step.data_len > step_data_buf->len) {
1317+
LOG_WRN("Step data appears malformed.");
1318+
return;
1319+
}
1320+
13161321
if (!func(&step, user_data)) {
13171322
return;
13181323
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
cmake_minimum_required(VERSION 3.20.0)
4+
5+
find_package(Zephyr COMPONENTS unittest REQUIRED HINTS $ENV{ZEPHYR_BASE})
6+
7+
project(bt_step_data_parse)
8+
9+
include_directories(BEFORE
10+
${ZEPHYR_BASE}/tests/bluetooth/host/cs/mocks
11+
)
12+
13+
add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/host host_mocks)
14+
add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/host/cs/mocks mocks)
15+
16+
target_link_libraries(testbinary PRIVATE mocks host_mocks)
17+
18+
target_sources(testbinary
19+
PRIVATE
20+
src/main.c
21+
22+
${ZEPHYR_BASE}/subsys/bluetooth/host/cs.c
23+
${ZEPHYR_BASE}/lib/net_buf/buf_simple.c
24+
${ZEPHYR_BASE}/subsys/logging/log_minimal.c
25+
)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CONFIG_ZTEST=y
2+
CONFIG_BT=y
3+
CONFIG_BT_HCI=y
4+
CONFIG_BT_CENTRAL=y
5+
CONFIG_BT_CHANNEL_SOUNDING=y
6+
CONFIG_ASSERT=y
7+
CONFIG_ASSERT_LEVEL=2
8+
CONFIG_ASSERT_VERBOSE=y
9+
CONFIG_ASSERT_ON_ERRORS=y
10+
CONFIG_NET_BUF=y
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
/*
2+
* Copyright (c) 2024 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include "mocks/conn.h"
8+
#include "mocks/hci_core.h"
9+
#include "mocks/net_buf.h"
10+
11+
#include <zephyr/kernel.h>
12+
#include <zephyr/bluetooth/cs.h>
13+
#include <zephyr/fff.h>
14+
15+
DEFINE_FFF_GLOBALS;
16+
17+
FAKE_VALUE_FUNC(bool, bt_le_cs_step_data_parse_func, struct bt_le_cs_subevent_step *, void *);
18+
19+
static void fff_reset_rule_before(const struct ztest_unit_test *test, void *fixture)
20+
{
21+
RESET_FAKE(bt_le_cs_step_data_parse_func);
22+
CONN_FFF_FAKES_LIST(RESET_FAKE);
23+
}
24+
25+
ZTEST_RULE(fff_reset_rule, fff_reset_rule_before, NULL);
26+
27+
ZTEST_SUITE(bt_le_cs_step_data_parse, NULL, NULL, NULL, NULL, NULL);
28+
29+
/*
30+
* Test empty data buffer
31+
*
32+
* Constraints:
33+
* - buffer len set to 0
34+
*
35+
* Expected behaviour:
36+
* - Callback function is not called
37+
*/
38+
ZTEST(bt_le_cs_step_data_parse, test_parsing_empty_buf)
39+
{
40+
struct net_buf_simple *buf = NET_BUF_SIMPLE(0);
41+
42+
bt_le_cs_step_data_parse(buf, bt_le_cs_step_data_parse_func, NULL);
43+
44+
zassert_equal(bt_le_cs_step_data_parse_func_fake.call_count, 0);
45+
}
46+
47+
/*
48+
* Test malformed step data
49+
*
50+
* Constraints:
51+
* - step data with a step length going out of bounds
52+
*
53+
* Expected behaviour:
54+
* - Callback function is called once
55+
*/
56+
ZTEST(bt_le_cs_step_data_parse, test_parsing_invalid_length)
57+
{
58+
struct net_buf_simple buf;
59+
uint8_t data[] = {
60+
0x00, 0x01, 0x01, 0x00, /* mode 0 */
61+
0x03, 0x20, 0x03, 0x00, 0x11, /* mode 3 step with bad length */
62+
};
63+
64+
bt_le_cs_step_data_parse_func_fake.return_val = true;
65+
66+
net_buf_simple_init_with_data(&buf, data, ARRAY_SIZE(data));
67+
68+
bt_le_cs_step_data_parse(&buf, bt_le_cs_step_data_parse_func, NULL);
69+
70+
zassert_equal(1, bt_le_cs_step_data_parse_func_fake.call_count, "called %d",
71+
bt_le_cs_step_data_parse_func_fake.call_count);
72+
}
73+
74+
/*
75+
* Test parsing stopped
76+
*
77+
* Constraints:
78+
* - Data contains valid step data
79+
* - Callback function returns false to stop parsing
80+
*
81+
* Expected behaviour:
82+
* - Once parsing is stopped, the callback is not called anymore
83+
*/
84+
ZTEST(bt_le_cs_step_data_parse, test_parsing_stopped)
85+
{
86+
struct net_buf_simple buf;
87+
uint8_t data[] = {
88+
0x00, 0x05, 0x01, 0x00, /* mode 0 */
89+
0x01, 0x10, 0x02, 0x00, 0x11, /* mode 1 */
90+
0x02, 0x11, 0x02, 0x00, 0x11, /* mode 2 */
91+
};
92+
93+
bt_le_cs_step_data_parse_func_fake.return_val = false;
94+
95+
net_buf_simple_init_with_data(&buf, data, ARRAY_SIZE(data));
96+
97+
bt_le_cs_step_data_parse(&buf, bt_le_cs_step_data_parse_func, NULL);
98+
99+
zassert_equal(1, bt_le_cs_step_data_parse_func_fake.call_count, "called %d",
100+
bt_le_cs_step_data_parse_func_fake.call_count);
101+
}
102+
103+
struct custom_user_data {
104+
const uint8_t *data;
105+
size_t len;
106+
};
107+
108+
static bool bt_le_cs_step_data_parse_func_custom_fake(struct bt_le_cs_subevent_step *step,
109+
void *user_data)
110+
{
111+
struct custom_user_data *ud = user_data;
112+
113+
/* mode check */
114+
zassert_true(ud->len-- > 0);
115+
zassert_equal(step->mode, *ud->data);
116+
ud->data++;
117+
118+
/* channel check */
119+
zassert_true(ud->len-- > 0);
120+
zassert_equal(step->channel, *ud->data);
121+
ud->data++;
122+
123+
/* step data length check */
124+
zassert_true(ud->len-- > 0);
125+
zassert_equal(step->data_len, *ud->data);
126+
ud->data++;
127+
128+
/* value check */
129+
zassert_true(ud->len >= step->data_len);
130+
zassert_mem_equal(step->data, ud->data, step->data_len);
131+
ud->data += step->data_len;
132+
ud->len -= step->data_len;
133+
134+
return true;
135+
}
136+
137+
/*
138+
* Test parsing successfully
139+
*
140+
* Constraints:
141+
* - Data contains valid step data
142+
* - Callback function returns false to stop parsing
143+
*
144+
* Expected behaviour:
145+
* - Data passed to the callback match the expected data
146+
*/
147+
ZTEST(bt_le_cs_step_data_parse, test_parsing_success)
148+
{
149+
struct net_buf_simple buf;
150+
uint8_t data[] = {
151+
0x00, 0x05, 0x01, 0x00, /* mode 0 */
152+
0x03, 0x11, 0x01, 0x11, /* mode 3 */
153+
0x02, 0x12, 0x02, 0x00, 0x11, /* mode 2 */
154+
0x03, 0x13, 0x01, 0x11, /* mode 3 */
155+
0x02, 0x14, 0x02, 0x00, 0x11, /* mode 2 */
156+
};
157+
158+
struct custom_user_data user_data = {
159+
.data = data,
160+
.len = ARRAY_SIZE(data),
161+
};
162+
163+
bt_le_cs_step_data_parse_func_fake.custom_fake = bt_le_cs_step_data_parse_func_custom_fake;
164+
165+
net_buf_simple_init_with_data(&buf, data, ARRAY_SIZE(data));
166+
167+
bt_le_cs_step_data_parse(&buf, bt_le_cs_step_data_parse_func, &user_data);
168+
169+
zassert_equal(5, bt_le_cs_step_data_parse_func_fake.call_count, "called %d",
170+
bt_le_cs_step_data_parse_func_fake.call_count);
171+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
common:
2+
tags:
3+
- bluetooth
4+
- host
5+
tests:
6+
bluetooth.host.cs.bt_le_cs_step_data_parse:
7+
type: unit
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
add_library(mocks STATIC
4+
conn.c
5+
hci_core.c
6+
net_buf.c
7+
)
8+
9+
target_include_directories(mocks PUBLIC
10+
..
11+
${ZEPHYR_BASE}/subsys/bluetooth
12+
${ZEPHYR_BASE}/subsys/bluetooth/host
13+
${ZEPHYR_BASE}/tests/bluetooth/host
14+
${ZEPHYR_BASE}/tests/bluetooth/host/cs/mocks
15+
)
16+
17+
target_link_libraries(mocks PRIVATE test_interface)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Copyright (c) 2024 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include "mocks/conn.h"
8+
9+
#include <zephyr/kernel.h>
10+
11+
DEFINE_FAKE_VOID_FUNC(bt_conn_unref, struct bt_conn *);
12+
DEFINE_FAKE_VALUE_FUNC(struct bt_conn *, bt_conn_lookup_handle, uint16_t, enum bt_conn_type);
13+
DEFINE_FAKE_VOID_FUNC(notify_remote_cs_capabilities, struct bt_conn *,
14+
struct bt_conn_le_cs_capabilities);
15+
DEFINE_FAKE_VOID_FUNC(notify_remote_cs_fae_table, struct bt_conn *, struct bt_conn_le_cs_fae_table);
16+
DEFINE_FAKE_VOID_FUNC(notify_cs_config_created, struct bt_conn *, struct bt_conn_le_cs_config *);
17+
DEFINE_FAKE_VOID_FUNC(notify_cs_config_removed, struct bt_conn *, uint8_t);
18+
DEFINE_FAKE_VOID_FUNC(notify_cs_subevent_result, struct bt_conn *,
19+
struct bt_conn_le_cs_subevent_result *);
20+
DEFINE_FAKE_VOID_FUNC(notify_cs_security_enable_available, struct bt_conn *);
21+
DEFINE_FAKE_VOID_FUNC(notify_cs_procedure_enable_available, struct bt_conn *,
22+
struct bt_conn_le_cs_procedure_enable_complete *);
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright (c) 2024 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <zephyr/bluetooth/conn.h>
8+
#include <zephyr/fff.h>
9+
#include <zephyr/kernel.h>
10+
11+
#include <host/conn_internal.h>
12+
13+
/* List of fakes used by this unit tester */
14+
#define CONN_FFF_FAKES_LIST(FAKE) \
15+
FAKE(bt_conn_unref) \
16+
FAKE(bt_conn_lookup_handle) \
17+
FAKE(notify_remote_cs_capabilities) \
18+
FAKE(notify_cs_config_created) \
19+
FAKE(notify_cs_config_removed) \
20+
FAKE(notify_cs_subevent_result) \
21+
FAKE(notify_cs_security_enable_available) \
22+
FAKE(notify_cs_procedure_enable_available) \
23+
FAKE(notify_remote_cs_fae_table)
24+
25+
DECLARE_FAKE_VOID_FUNC(bt_conn_unref, struct bt_conn *);
26+
DECLARE_FAKE_VALUE_FUNC(struct bt_conn *, bt_conn_lookup_handle, uint16_t, enum bt_conn_type);
27+
DECLARE_FAKE_VOID_FUNC(notify_remote_cs_capabilities, struct bt_conn *,
28+
struct bt_conn_le_cs_capabilities);
29+
DECLARE_FAKE_VOID_FUNC(notify_remote_cs_fae_table, struct bt_conn *,
30+
struct bt_conn_le_cs_fae_table);
31+
DECLARE_FAKE_VOID_FUNC(notify_cs_config_created, struct bt_conn *, struct bt_conn_le_cs_config *);
32+
DECLARE_FAKE_VOID_FUNC(notify_cs_config_removed, struct bt_conn *, uint8_t);
33+
DECLARE_FAKE_VOID_FUNC(notify_cs_subevent_result, struct bt_conn *,
34+
struct bt_conn_le_cs_subevent_result *);
35+
DECLARE_FAKE_VOID_FUNC(notify_cs_security_enable_available, struct bt_conn *);
36+
DECLARE_FAKE_VOID_FUNC(notify_cs_procedure_enable_available, struct bt_conn *,
37+
struct bt_conn_le_cs_procedure_enable_complete *);
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
* Copyright (c) 2024 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include "mocks/hci_core.h"
8+
9+
#include <zephyr/bluetooth/hci.h>
10+
#include <zephyr/kernel.h>
11+
12+
#include <host/hci_core.h>
13+
14+
struct bt_dev bt_dev = {
15+
.manufacturer = 0x1234,
16+
};
17+
18+
DEFINE_FAKE_VALUE_FUNC(struct net_buf *, bt_hci_cmd_create, uint16_t, uint8_t);
19+
DEFINE_FAKE_VALUE_FUNC(int, bt_hci_cmd_send_sync, uint16_t, struct net_buf *, struct net_buf **);
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*
2+
* Copyright (c) 2024 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <zephyr/fff.h>
8+
#include <zephyr/kernel.h>
9+
10+
/* List of fakes used by this unit tester */
11+
#define HCI_CORE_FFF_FAKES_LIST(FAKE) \
12+
FAKE(bt_hci_cmd_create) \
13+
FAKE(bt_hci_cmd_send_sync)
14+
15+
DECLARE_FAKE_VALUE_FUNC(struct net_buf *, bt_hci_cmd_create, uint16_t, uint8_t);
16+
DECLARE_FAKE_VALUE_FUNC(int, bt_hci_cmd_send_sync, uint16_t, struct net_buf *, struct net_buf **);

0 commit comments

Comments
 (0)