Skip to content

Commit

Permalink
fix: Don't use memcmp to compare IP_Ports.
Browse files Browse the repository at this point in the history
`memcmp` compares padding bytes as well, which may be arbitrary or
uninitialised.
  • Loading branch information
iphydf committed Jan 29, 2024
1 parent d94246a commit 317ac88
Show file tree
Hide file tree
Showing 12 changed files with 196 additions and 17 deletions.
2 changes: 1 addition & 1 deletion toxcore/TCP_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -1043,7 +1043,7 @@ TCP_Server *new_tcp_server(const Logger *logger, const Memory *mem, const Random
memcpy(temp->secret_key, secret_key, CRYPTO_SECRET_KEY_SIZE);
crypto_derive_public_key(temp->public_key, temp->secret_key);

bs_list_init(&temp->accepted_key_list, CRYPTO_PUBLIC_KEY_SIZE, 8);
bs_list_init(&temp->accepted_key_list, CRYPTO_PUBLIC_KEY_SIZE, 8, memcmp);

return temp;
}
Expand Down
7 changes: 1 addition & 6 deletions toxcore/group.c
Original file line number Diff line number Diff line change
Expand Up @@ -956,11 +956,6 @@ static bool delpeer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, void
return true;
}

static int cmp_u64(uint64_t a, uint64_t b)
{
return (a > b ? 1 : 0) - (a < b ? 1 : 0);
}

/** Order peers with friends first and with more recently active earlier */
non_null()
static int cmp_frozen(const void *a, const void *b)
Expand All @@ -972,7 +967,7 @@ static int cmp_frozen(const void *a, const void *b)
return pa->is_friend ? -1 : 1;
}

return cmp_u64(pb->last_active, pa->last_active);
return cmp_uint(pb->last_active, pa->last_active);
}

/** @brief Delete frozen peers as necessary to ensure at most `g->maxfrozen` remain.
Expand Down
5 changes: 3 additions & 2 deletions toxcore/list.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ static int find(const BS_List *list, const uint8_t *data)
// closest match is found if we move back to where we have already been

while (true) {
const int r = memcmp(data, list->data + list->element_size * i, list->element_size);
const int r = list->cmp_callback(data, list->data + list->element_size * i, list->element_size);

if (r == 0) {
return i;
Expand Down Expand Up @@ -135,14 +135,15 @@ static bool resize(BS_List *list, uint32_t new_size)
}


int bs_list_init(BS_List *list, uint32_t element_size, uint32_t initial_capacity)
int bs_list_init(BS_List *list, uint32_t element_size, uint32_t initial_capacity, bs_list_cmp_cb *cmp_callback)
{
// set initial values
list->n = 0;
list->element_size = element_size;
list->capacity = 0;
list->data = nullptr;
list->ids = nullptr;
list->cmp_callback = cmp_callback;

if (initial_capacity != 0) {
if (!resize(list, initial_capacity)) {
Expand Down
6 changes: 5 additions & 1 deletion toxcore/list.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#define C_TOXCORE_TOXCORE_LIST_H

#include <stdbool.h>
#include <stddef.h> // size_t
#include <stdint.h>

#include "attributes.h"
Expand All @@ -20,12 +21,15 @@
extern "C" {
#endif

typedef int bs_list_cmp_cb(const void *a, const void *b, size_t size);

typedef struct BS_List {
uint32_t n; // number of elements
uint32_t capacity; // number of elements memory is allocated for
uint32_t element_size; // size of the elements
uint8_t *data; // array of elements
int *ids; // array of element ids
bs_list_cmp_cb *cmp_callback;
} BS_List;

/** @brief Initialize a list.
Expand All @@ -37,7 +41,7 @@ typedef struct BS_List {
* @retval 0 failure
*/
non_null()
int bs_list_init(BS_List *list, uint32_t element_size, uint32_t initial_capacity);
int bs_list_init(BS_List *list, uint32_t element_size, uint32_t initial_capacity, bs_list_cmp_cb *cmp_callback);

/** Free a list initiated with list_init */
nullable(1)
Expand Down
6 changes: 3 additions & 3 deletions toxcore/list_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,21 @@ namespace {
TEST(List, CreateAndDestroyWithNonZeroSize)
{
BS_List list;
bs_list_init(&list, sizeof(int), 10);
bs_list_init(&list, sizeof(int), 10, memcmp);
bs_list_free(&list);
}

TEST(List, CreateAndDestroyWithZeroSize)
{
BS_List list;
bs_list_init(&list, sizeof(int), 0);
bs_list_init(&list, sizeof(int), 0, memcmp);
bs_list_free(&list);
}

TEST(List, DeleteFromEmptyList)
{
BS_List list;
bs_list_init(&list, sizeof(int), 0);
bs_list_init(&list, sizeof(int), 0, memcmp);
const uint8_t data[sizeof(int)] = {0};
bs_list_remove(&list, data, 0);
bs_list_free(&list);
Expand Down
2 changes: 1 addition & 1 deletion toxcore/net_crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -3163,7 +3163,7 @@ Net_Crypto *new_net_crypto(const Logger *log, const Memory *mem, const Random *r
networking_registerhandler(dht_get_net(dht), NET_PACKET_CRYPTO_HS, &udp_handle_packet, temp);
networking_registerhandler(dht_get_net(dht), NET_PACKET_CRYPTO_DATA, &udp_handle_packet, temp);

bs_list_init(&temp->ip_port_list, sizeof(IP_Port), 8);
bs_list_init(&temp->ip_port_list, sizeof(IP_Port), 8, ipport_cmp_handler);

return temp;
}
Expand Down
64 changes: 63 additions & 1 deletion toxcore/network.c
Original file line number Diff line number Diff line change
Expand Up @@ -1435,6 +1435,61 @@ bool ipport_equal(const IP_Port *a, const IP_Port *b)
return ip_equal(&a->ip, &b->ip);
}

non_null()
static int ip4_cmp(const IP4 *a, const IP4 *b)
{
return cmp_uint(a->uint32, b->uint32);
}

non_null()
static int ip6_cmp(const IP6 *a, const IP6 *b)
{
const int res = cmp_uint(a->uint64[0], b->uint64[0]);
if (res != 0) {
return res;
}
return cmp_uint(a->uint64[1], b->uint64[1]);
}

non_null()
static int ip_cmp(const IP *a, const IP *b)
{
const int res = cmp_uint(a->family.value, b->family.value);
if (res != 0) {
return res;
}
switch (a->family.value) {
case TOX_AF_UNSPEC:
return 0;
case TOX_AF_INET:
case TCP_INET:
case TOX_TCP_INET:
return ip4_cmp(&a->ip.v4, &b->ip.v4);
case TOX_AF_INET6:
case TCP_INET6:
case TOX_TCP_INET6:
case TCP_SERVER_FAMILY: // these happen to be ipv6 according to TCP_server.c.
case TCP_CLIENT_FAMILY:
return ip6_cmp(&a->ip.v6, &b->ip.v6);
}
// Invalid, we don't compare any further and consider them equal.
return 0;
}

int ipport_cmp_handler(const void *a, const void *b, size_t size)
{
const IP_Port *ipp_a = (const IP_Port *)a;
const IP_Port *ipp_b = (const IP_Port *)b;
assert(size == sizeof(IP_Port));

const int ip_res = ip_cmp(&ipp_a->ip, &ipp_b->ip);
if (ip_res != 0) {
return ip_res;
}

return cmp_uint(ipp_a->port, ipp_b->port);
}

/** nulls out ip */
void ip_reset(IP *ip)
{
Expand Down Expand Up @@ -1508,7 +1563,14 @@ void ipport_copy(IP_Port *target, const IP_Port *source)
return;
}

*target = *source;
// Write to a temporary object first, so that padding bytes are
// uninitialised and msan can catch mistakes in downstream code.
IP_Port tmp;
tmp.ip.family = source->ip.family;
tmp.ip.ip = source->ip.ip;
tmp.port = source->port;

*target = tmp;
}

const char *net_ip_ntoa(const IP *ip, Ip_Ntoa *ip_str)
Expand Down
16 changes: 14 additions & 2 deletions toxcore/network.h
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ non_null()
bool addr_parse_ip(const char *address, IP *to);

/**
* Compares two IPAny structures.
* Compares two IP structures.
*
* Unset means unequal.
*
Expand All @@ -347,7 +347,7 @@ nullable(1, 2)
bool ip_equal(const IP *a, const IP *b);

/**
* Compares two IPAny_Port structures.
* Compares two IP_Port structures.
*
* Unset means unequal.
*
Expand All @@ -356,6 +356,18 @@ bool ip_equal(const IP *a, const IP *b);
nullable(1, 2)
bool ipport_equal(const IP_Port *a, const IP_Port *b);

/**
* @brief IP_Port comparison function with `memcmp` signature.
*
* Casts the void pointers to `IP_Port *` for comparison.
*
* @retval -1 if `a < b`
* @retval 0 if `a == b`
* @retval 1 if `a > b`
*/
non_null()
int ipport_cmp_handler(const void *a, const void *b, size_t size);

/** nulls out ip */
non_null()
void ip_reset(IP *ip);
Expand Down
86 changes: 86 additions & 0 deletions toxcore/network_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,90 @@ TEST(IpParseAddr, FormatsIPv6)
EXPECT_EQ(std::string(ip_str), "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
}

TEST(IpportCmp, BehavesLikeMemcmp)
{
auto cmp_val = [](int val) { return val < 0 ? -1 : val > 0 ? 1 : 0; };

IP_Port a = {0};
IP_Port b = {0};

a.ip.family = net_family_ipv4();
b.ip.family = net_family_ipv4();

a.port = 10;
b.port = 20;

EXPECT_EQ( //
ipport_cmp_handler(&a, &b, sizeof(IP_Port)), -1)
<< "a=" << a << "\n"
<< "b=" << b;
EXPECT_EQ( //
ipport_cmp_handler(&a, &b, sizeof(IP_Port)), //
cmp_val(memcmp(&a, &b, sizeof(IP_Port))))
<< "a=" << a << "\n"
<< "b=" << b;

a.ip.ip.v4.uint8[0] = 192;
b.ip.ip.v4.uint8[0] = 10;

EXPECT_EQ( //
ipport_cmp_handler(&a, &b, sizeof(IP_Port)), 1)
<< "a=" << a << "\n"
<< "b=" << b;
EXPECT_EQ( //
ipport_cmp_handler(&a, &b, sizeof(IP_Port)), //
cmp_val(memcmp(&a, &b, sizeof(IP_Port))))
<< "a=" << a << "\n"
<< "b=" << b;
}

TEST(IpportCmp, Ipv6BeginAndEndCompareCorrectly)
{
IP_Port a = {0};
IP_Port b = {0};

a.ip.family = net_family_ipv6();
b.ip.family = net_family_ipv6();

a.ip.ip.v6.uint8[0] = 0xab;
b.ip.ip.v6.uint8[0] = 0xba;

EXPECT_EQ(ipport_cmp_handler(&a, &b, sizeof(IP_Port)), -1);

a.ip.ip.v6.uint8[0] = 0;
b.ip.ip.v6.uint8[0] = 0;

a.ip.ip.v6.uint8[15] = 0xba;

EXPECT_EQ(ipport_cmp_handler(&a, &b, sizeof(IP_Port)), 1);
}

TEST(IpportCmp, UnspecAlwaysComparesEqual)
{
IP_Port a = {0};
IP_Port b = {0};

a.ip.family = net_family_unspec();
b.ip.family = net_family_unspec();

a.ip.ip.v4.uint8[0] = 0xab;
b.ip.ip.v4.uint8[0] = 0xba;

EXPECT_EQ(ipport_cmp_handler(&a, &b, sizeof(IP_Port)), 0);
}

TEST(IpportCmp, InvalidAlwaysComparesEqual)
{
IP_Port a = {0};
IP_Port b = {0};

a.ip.family.value = 0xff;
b.ip.family.value = 0xff;

a.ip.ip.v4.uint8[0] = 0xab;
b.ip.ip.v4.uint8[0] = 0xba;

EXPECT_EQ(ipport_cmp_handler(&a, &b, sizeof(IP_Port)), 0);
}

} // namespace
5 changes: 5 additions & 0 deletions toxcore/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,11 @@ uint64_t min_u64(uint64_t a, uint64_t b)
return a < b ? a : b;
}

int cmp_uint(uint64_t a, uint64_t b)
{
return (a > b ? 1 : 0) - (a < b ? 1 : 0);
}

uint32_t jenkins_one_at_a_time_hash(const uint8_t *key, size_t len)
{
uint32_t hash = 0;
Expand Down
3 changes: 3 additions & 0 deletions toxcore/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ uint16_t min_u16(uint16_t a, uint16_t b);
uint32_t min_u32(uint32_t a, uint32_t b);
uint64_t min_u64(uint64_t a, uint64_t b);

// Comparison function: return -1 if a<b, 0 if a==b, 1 if a>b.
int cmp_uint(uint64_t a, uint64_t b);

/** @brief Returns a 32-bit hash of key of size len */
non_null()
uint32_t jenkins_one_at_a_time_hash(const uint8_t *key, size_t len);
Expand Down
11 changes: 11 additions & 0 deletions toxcore/util_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,15 @@ TEST(Util, IdCopyMakesKeysEqual)
EXPECT_TRUE(pk_equal(pk1, pk2));
}

TEST(Cmp, OrdersNumbersCorrectly)
{
EXPECT_EQ(cmp_uint(1, 2), -1);
EXPECT_EQ(cmp_uint(0, UINT32_MAX), -1);
EXPECT_EQ(cmp_uint(UINT32_MAX, 0), 1);
EXPECT_EQ(cmp_uint(UINT32_MAX, UINT32_MAX), 0);
EXPECT_EQ(cmp_uint(0, UINT64_MAX), -1);
EXPECT_EQ(cmp_uint(UINT64_MAX, 0), 1);
EXPECT_EQ(cmp_uint(UINT64_MAX, UINT64_MAX), 0);
}

} // namespace

0 comments on commit 317ac88

Please sign in to comment.