mirror of
https://github.com/irungentoo/toxcore.git
synced 2024-03-22 13:30:51 +08:00
refactor: Simplify locking logic in DHT
This fixes an error reported by cppcheck about overflowing the callback array. The locking logic probably was broken, so I replaced it with something simpler and more robust.
This commit is contained in:
parent
0f1db935a9
commit
4062c3a2ba
|
@ -500,10 +500,10 @@ static void dht_pk_callback(void *object, int32_t number, const uint8_t *dht_pub
|
||||||
{
|
{
|
||||||
if ((NUM_FIRST == number && !first) || (NUM_LAST == number && !last)) {
|
if ((NUM_FIRST == number && !first) || (NUM_LAST == number && !last)) {
|
||||||
Onions *on = (Onions *)object;
|
Onions *on = (Onions *)object;
|
||||||
uint16_t count = 0;
|
uint32_t token = 0;
|
||||||
int ret = dht_addfriend(on->onion->dht, dht_public_key, &dht_ip_callback, object, number, &count);
|
int ret = dht_addfriend(on->onion->dht, dht_public_key, &dht_ip_callback, object, number, &token);
|
||||||
ck_assert_msg(ret == 0, "dht_addfriend() did not return 0");
|
ck_assert_msg(ret == 0, "dht_addfriend() did not return 0");
|
||||||
ck_assert_msg(count == 1, "Count not 1, count is %u", count);
|
ck_assert_msg(token == 1, "Count not 1, count is %u", token);
|
||||||
|
|
||||||
if (NUM_FIRST == number && !first) {
|
if (NUM_FIRST == number && !first) {
|
||||||
first = 1;
|
first = 1;
|
||||||
|
|
|
@ -43,6 +43,9 @@
|
||||||
/** Number of get node requests to send to quickly find close nodes. */
|
/** Number of get node requests to send to quickly find close nodes. */
|
||||||
#define MAX_BOOTSTRAP_TIMES 5
|
#define MAX_BOOTSTRAP_TIMES 5
|
||||||
|
|
||||||
|
// TODO(sudden6): find out why we need multiple callbacks and if we really need 32
|
||||||
|
#define DHT_FRIEND_MAX_LOCKS 32
|
||||||
|
|
||||||
typedef struct DHT_Friend_Callback {
|
typedef struct DHT_Friend_Callback {
|
||||||
dht_ip_cb *ip_callback;
|
dht_ip_cb *ip_callback;
|
||||||
void *data;
|
void *data;
|
||||||
|
@ -61,7 +64,8 @@ struct DHT_Friend {
|
||||||
/* Symmetric NAT hole punching stuff. */
|
/* Symmetric NAT hole punching stuff. */
|
||||||
NAT nat;
|
NAT nat;
|
||||||
|
|
||||||
uint16_t lock_count;
|
/* Each set bit represents one installed callback */
|
||||||
|
uint32_t lock_flags;
|
||||||
DHT_Friend_Callback callbacks[DHT_FRIEND_MAX_LOCKS];
|
DHT_Friend_Callback callbacks[DHT_FRIEND_MAX_LOCKS];
|
||||||
|
|
||||||
Node_format to_bootstrap[MAX_SENT_NODES];
|
Node_format to_bootstrap[MAX_SENT_NODES];
|
||||||
|
@ -71,6 +75,8 @@ struct DHT_Friend {
|
||||||
static const DHT_Friend empty_dht_friend = {{0}};
|
static const DHT_Friend empty_dht_friend = {{0}};
|
||||||
const Node_format empty_node_format = {{0}};
|
const Node_format empty_node_format = {{0}};
|
||||||
|
|
||||||
|
static_assert(sizeof (empty_dht_friend.lock_flags) * 8 == DHT_FRIEND_MAX_LOCKS, "Bitfield size and number of locks don't match");
|
||||||
|
|
||||||
typedef struct Cryptopacket_Handler {
|
typedef struct Cryptopacket_Handler {
|
||||||
cryptopacket_handler_cb *function;
|
cryptopacket_handler_cb *function;
|
||||||
void *object;
|
void *object;
|
||||||
|
@ -1419,8 +1425,9 @@ uint32_t addto_lists(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key
|
||||||
return used;
|
return used;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint32_t i = 0; i < friend_foundip->lock_count; ++i) {
|
for (uint32_t i = 0; i < DHT_FRIEND_MAX_LOCKS; ++i) {
|
||||||
if (friend_foundip->callbacks[i].ip_callback != nullptr) {
|
const bool has_lock = (friend_foundip->lock_flags & (UINT32_C(1) << i)) > 0;
|
||||||
|
if (has_lock && friend_foundip->callbacks[i].ip_callback != nullptr) {
|
||||||
friend_foundip->callbacks[i].ip_callback(friend_foundip->callbacks[i].data,
|
friend_foundip->callbacks[i].ip_callback(friend_foundip->callbacks[i].data,
|
||||||
friend_foundip->callbacks[i].number, &ipp_copy);
|
friend_foundip->callbacks[i].number, &ipp_copy);
|
||||||
}
|
}
|
||||||
|
@ -1745,35 +1752,75 @@ static int handle_sendnodes_ipv6(void *object, const IP_Port *source, const uint
|
||||||
/*----------------------------------------------------------------------------------*/
|
/*----------------------------------------------------------------------------------*/
|
||||||
/*------------------------END of packet handling functions--------------------------*/
|
/*------------------------END of packet handling functions--------------------------*/
|
||||||
|
|
||||||
non_null(1) nullable(2, 3, 5)
|
non_null(1) nullable(2, 3)
|
||||||
static void dht_friend_lock(DHT_Friend *const dht_friend, dht_ip_cb *ip_callback,
|
static uint32_t dht_friend_lock(DHT_Friend *const dht_friend, dht_ip_cb *ip_callback,
|
||||||
void *data, int32_t number, uint16_t *lock_count)
|
void *data, int32_t number)
|
||||||
{
|
{
|
||||||
const uint16_t lock_num = dht_friend->lock_count;
|
// find first free slot
|
||||||
++dht_friend->lock_count;
|
uint8_t lock_num;
|
||||||
|
uint32_t lock_token = 0;
|
||||||
|
for (lock_num = 0; lock_num < DHT_FRIEND_MAX_LOCKS; ++lock_num) {
|
||||||
|
lock_token = UINT32_C(1) << lock_num;
|
||||||
|
if ((dht_friend->lock_flags & lock_token) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// One of the conditions would be enough, but static analyzers don't get that
|
||||||
|
if (lock_token == 0 || lock_num == DHT_FRIEND_MAX_LOCKS) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Claim that slot
|
||||||
|
dht_friend->lock_flags |= lock_token;
|
||||||
|
|
||||||
dht_friend->callbacks[lock_num].ip_callback = ip_callback;
|
dht_friend->callbacks[lock_num].ip_callback = ip_callback;
|
||||||
dht_friend->callbacks[lock_num].data = data;
|
dht_friend->callbacks[lock_num].data = data;
|
||||||
dht_friend->callbacks[lock_num].number = number;
|
dht_friend->callbacks[lock_num].number = number;
|
||||||
|
|
||||||
if (lock_count != nullptr) {
|
return lock_token;
|
||||||
*lock_count = lock_num + 1;
|
}
|
||||||
|
|
||||||
|
non_null()
|
||||||
|
static void dht_friend_unlock(DHT_Friend *const dht_friend, uint32_t lock_token)
|
||||||
|
{
|
||||||
|
// If this triggers, there was a double free
|
||||||
|
assert((lock_token & dht_friend->lock_flags) > 0);
|
||||||
|
|
||||||
|
// find used slot
|
||||||
|
uint8_t lock_num;
|
||||||
|
for (lock_num = 0; lock_num < DHT_FRIEND_MAX_LOCKS; ++lock_num) {
|
||||||
|
if ((dht_friend->lock_flags & lock_token) > 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lock_num == DHT_FRIEND_MAX_LOCKS) {
|
||||||
|
// Gracefully handle double unlock
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the slot
|
||||||
|
dht_friend->lock_flags &= ~lock_token;
|
||||||
|
|
||||||
|
dht_friend->callbacks[lock_num].ip_callback = nullptr;
|
||||||
|
dht_friend->callbacks[lock_num].data = nullptr;
|
||||||
|
dht_friend->callbacks[lock_num].number = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int dht_addfriend(DHT *dht, const uint8_t *public_key, dht_ip_cb *ip_callback,
|
int dht_addfriend(DHT *dht, const uint8_t *public_key, dht_ip_cb *ip_callback,
|
||||||
void *data, int32_t number, uint16_t *lock_count)
|
void *data, int32_t number, uint32_t *lock_token)
|
||||||
{
|
{
|
||||||
const uint32_t friend_num = index_of_friend_pk(dht->friends_list, dht->num_friends, public_key);
|
const uint32_t friend_num = index_of_friend_pk(dht->friends_list, dht->num_friends, public_key);
|
||||||
|
|
||||||
if (friend_num != UINT32_MAX) { /* Is friend already in DHT? */
|
if (friend_num != UINT32_MAX) { /* Is friend already in DHT? */
|
||||||
DHT_Friend *const dht_friend = &dht->friends_list[friend_num];
|
DHT_Friend *const dht_friend = &dht->friends_list[friend_num];
|
||||||
|
const uint32_t tmp_lock_token = dht_friend_lock(dht_friend, ip_callback, data, number);
|
||||||
|
|
||||||
if (dht_friend->lock_count == DHT_FRIEND_MAX_LOCKS) {
|
if (tmp_lock_token == 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
dht_friend_lock(dht_friend, ip_callback, data, number, lock_count);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1791,7 +1838,8 @@ int dht_addfriend(DHT *dht, const uint8_t *public_key, dht_ip_cb *ip_callback,
|
||||||
dht_friend->nat.nat_ping_id = random_u64(dht->rng);
|
dht_friend->nat.nat_ping_id = random_u64(dht->rng);
|
||||||
++dht->num_friends;
|
++dht->num_friends;
|
||||||
|
|
||||||
dht_friend_lock(dht_friend, ip_callback, data, number, lock_count);
|
*lock_token = dht_friend_lock(dht_friend, ip_callback, data, number);
|
||||||
|
assert(*lock_token != 0); // Friend was newly allocated
|
||||||
|
|
||||||
dht_friend->num_to_bootstrap = get_close_nodes(dht, dht_friend->public_key, dht_friend->to_bootstrap, net_family_unspec(),
|
dht_friend->num_to_bootstrap = get_close_nodes(dht, dht_friend->public_key, dht_friend->to_bootstrap, net_family_unspec(),
|
||||||
true, false);
|
true, false);
|
||||||
|
@ -1799,7 +1847,7 @@ int dht_addfriend(DHT *dht, const uint8_t *public_key, dht_ip_cb *ip_callback,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int dht_delfriend(DHT *dht, const uint8_t *public_key, uint16_t lock_count)
|
int dht_delfriend(DHT *dht, const uint8_t *public_key, uint32_t lock_token)
|
||||||
{
|
{
|
||||||
const uint32_t friend_num = index_of_friend_pk(dht->friends_list, dht->num_friends, public_key);
|
const uint32_t friend_num = index_of_friend_pk(dht->friends_list, dht->num_friends, public_key);
|
||||||
|
|
||||||
|
@ -1808,13 +1856,9 @@ int dht_delfriend(DHT *dht, const uint8_t *public_key, uint16_t lock_count)
|
||||||
}
|
}
|
||||||
|
|
||||||
DHT_Friend *const dht_friend = &dht->friends_list[friend_num];
|
DHT_Friend *const dht_friend = &dht->friends_list[friend_num];
|
||||||
--dht_friend->lock_count;
|
dht_friend_unlock(dht_friend, lock_token);
|
||||||
|
if (dht_friend->lock_flags > 0) {
|
||||||
if (dht_friend->lock_count > 0 && lock_count > 0) { /* DHT friend is still in use.*/
|
/* DHT friend is still in use.*/
|
||||||
--lock_count;
|
|
||||||
dht_friend->callbacks[lock_count].ip_callback = nullptr;
|
|
||||||
dht_friend->callbacks[lock_count].data = nullptr;
|
|
||||||
dht_friend->callbacks[lock_count].number = 0;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2765,7 +2809,8 @@ DHT *new_dht(const Logger *log, const Random *rng, const Network *ns, Mono_Time
|
||||||
|
|
||||||
crypto_new_keypair(rng, random_public_key_bytes, random_secret_key_bytes);
|
crypto_new_keypair(rng, random_public_key_bytes, random_secret_key_bytes);
|
||||||
|
|
||||||
if (dht_addfriend(dht, random_public_key_bytes, nullptr, nullptr, 0, nullptr) != 0) {
|
uint32_t token; // We don't intend to delete these ever, but need to pass the token
|
||||||
|
if (dht_addfriend(dht, random_public_key_bytes, nullptr, nullptr, 0, &token) != 0) {
|
||||||
kill_dht(dht);
|
kill_dht(dht);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,8 +172,6 @@ typedef struct NAT {
|
||||||
uint64_t nat_ping_timestamp;
|
uint64_t nat_ping_timestamp;
|
||||||
} NAT;
|
} NAT;
|
||||||
|
|
||||||
#define DHT_FRIEND_MAX_LOCKS 32
|
|
||||||
|
|
||||||
typedef struct Node_format {
|
typedef struct Node_format {
|
||||||
uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE];
|
uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE];
|
||||||
IP_Port ip_port;
|
IP_Port ip_port;
|
||||||
|
@ -327,29 +325,34 @@ non_null(1) nullable(2)
|
||||||
void dht_callback_get_nodes_response(DHT *dht, dht_get_nodes_response_cb *function);
|
void dht_callback_get_nodes_response(DHT *dht, dht_get_nodes_response_cb *function);
|
||||||
|
|
||||||
/** @brief Add a new friend to the friends list.
|
/** @brief Add a new friend to the friends list.
|
||||||
* public_key must be CRYPTO_PUBLIC_KEY_SIZE bytes long.
|
* @param public_key must be CRYPTO_PUBLIC_KEY_SIZE bytes long.
|
||||||
*
|
*
|
||||||
* ip_callback is the callback of a function that will be called when the ip address
|
* @param ip_callback is the callback of a function that will be called when the ip address
|
||||||
* is found along with arguments data and number.
|
* is found along with arguments data and number.
|
||||||
|
* @param data User data for the callback
|
||||||
|
* @param number Will be passed to ip_callback
|
||||||
*
|
*
|
||||||
* lock_count will be set to a non zero number that must be passed to `dht_delfriend()`
|
* @param lock_token will be set to a non zero number that must be passed to `dht_delfriend()`
|
||||||
* to properly remove the callback.
|
* to properly remove the callback.
|
||||||
*
|
*
|
||||||
* @retval 0 if success.
|
* @retval 0 if success.
|
||||||
* @retval -1 if failure (friends list is full).
|
* @retval -1 if failure (friends list is full).
|
||||||
*/
|
*/
|
||||||
non_null(1, 2) nullable(3, 4, 6)
|
non_null(1, 2, 6) nullable(3, 4)
|
||||||
int dht_addfriend(DHT *dht, const uint8_t *public_key, dht_ip_cb *ip_callback,
|
int dht_addfriend(DHT *dht, const uint8_t *public_key, dht_ip_cb *ip_callback,
|
||||||
void *data, int32_t number, uint16_t *lock_count);
|
void *data, int32_t number, uint32_t *lock_token);
|
||||||
|
|
||||||
/** @brief Delete a friend from the friends list.
|
/** @brief Delete a friend from the friends list.
|
||||||
* public_key must be CRYPTO_PUBLIC_KEY_SIZE bytes long.
|
* public_key must be CRYPTO_PUBLIC_KEY_SIZE bytes long.
|
||||||
|
* @param dht The DHT object
|
||||||
|
* @param public_key The public key of the friend
|
||||||
|
* @param lock_token The token received by dht_addfriend(...)
|
||||||
*
|
*
|
||||||
* @retval 0 if success.
|
* @retval 0 if success.
|
||||||
* @retval -1 if failure (public_key not in friends list).
|
* @retval -1 if failure (public_key not in friends list).
|
||||||
*/
|
*/
|
||||||
non_null()
|
non_null()
|
||||||
int dht_delfriend(DHT *dht, const uint8_t *public_key, uint16_t lock_count);
|
int dht_delfriend(DHT *dht, const uint8_t *public_key, uint32_t lock_token);
|
||||||
|
|
||||||
/** @brief Get ip of friend.
|
/** @brief Get ip of friend.
|
||||||
*
|
*
|
||||||
|
|
|
@ -31,7 +31,7 @@ struct Friend_Conn {
|
||||||
|
|
||||||
uint8_t real_public_key[CRYPTO_PUBLIC_KEY_SIZE];
|
uint8_t real_public_key[CRYPTO_PUBLIC_KEY_SIZE];
|
||||||
uint8_t dht_temp_pk[CRYPTO_PUBLIC_KEY_SIZE];
|
uint8_t dht_temp_pk[CRYPTO_PUBLIC_KEY_SIZE];
|
||||||
uint16_t dht_lock;
|
uint32_t dht_lock_token;
|
||||||
IP_Port dht_ip_port;
|
IP_Port dht_ip_port;
|
||||||
uint64_t dht_pk_lastrecv;
|
uint64_t dht_pk_lastrecv;
|
||||||
uint64_t dht_ip_port_lastrecv;
|
uint64_t dht_ip_port_lastrecv;
|
||||||
|
@ -377,16 +377,15 @@ static void change_dht_pk(Friend_Connections *fr_c, int friendcon_id, const uint
|
||||||
|
|
||||||
friend_con->dht_pk_lastrecv = mono_time_get(fr_c->mono_time);
|
friend_con->dht_pk_lastrecv = mono_time_get(fr_c->mono_time);
|
||||||
|
|
||||||
if (friend_con->dht_lock > 0) {
|
if (friend_con->dht_lock_token > 0) {
|
||||||
if (dht_delfriend(fr_c->dht, friend_con->dht_temp_pk, friend_con->dht_lock) != 0) {
|
if (dht_delfriend(fr_c->dht, friend_con->dht_temp_pk, friend_con->dht_lock_token) != 0) {
|
||||||
LOGGER_ERROR(fr_c->logger, "a. Could not delete dht peer. Please report this.");
|
LOGGER_ERROR(fr_c->logger, "a. Could not delete dht peer. Please report this.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
friend_con->dht_lock_token = 0;
|
||||||
friend_con->dht_lock = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dht_addfriend(fr_c->dht, dht_public_key, dht_ip_callback, fr_c, friendcon_id, &friend_con->dht_lock);
|
dht_addfriend(fr_c->dht, dht_public_key, dht_ip_callback, fr_c, friendcon_id, &friend_con->dht_lock_token);
|
||||||
memcpy(friend_con->dht_temp_pk, dht_public_key, CRYPTO_PUBLIC_KEY_SIZE);
|
memcpy(friend_con->dht_temp_pk, dht_public_key, CRYPTO_PUBLIC_KEY_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -609,7 +608,7 @@ static int friend_new_connection(Friend_Connections *fr_c, int friendcon_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If dht_temp_pk does not contains a pk. */
|
/* If dht_temp_pk does not contains a pk. */
|
||||||
if (friend_con->dht_lock == 0) {
|
if (friend_con->dht_lock_token == 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -838,8 +837,9 @@ int kill_friend_connection(Friend_Connections *fr_c, int friendcon_id)
|
||||||
onion_delfriend(fr_c->onion_c, friend_con->onion_friendnum);
|
onion_delfriend(fr_c->onion_c, friend_con->onion_friendnum);
|
||||||
crypto_kill(fr_c->net_crypto, friend_con->crypt_connection_id);
|
crypto_kill(fr_c->net_crypto, friend_con->crypt_connection_id);
|
||||||
|
|
||||||
if (friend_con->dht_lock > 0) {
|
if (friend_con->dht_lock_token > 0) {
|
||||||
dht_delfriend(fr_c->dht, friend_con->dht_temp_pk, friend_con->dht_lock);
|
dht_delfriend(fr_c->dht, friend_con->dht_temp_pk, friend_con->dht_lock_token);
|
||||||
|
friend_con->dht_lock_token = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return wipe_friend_conn(fr_c, friendcon_id);
|
return wipe_friend_conn(fr_c, friendcon_id);
|
||||||
|
@ -967,9 +967,9 @@ void do_friend_connections(Friend_Connections *fr_c, void *userdata)
|
||||||
if (friend_con != nullptr) {
|
if (friend_con != nullptr) {
|
||||||
if (friend_con->status == FRIENDCONN_STATUS_CONNECTING) {
|
if (friend_con->status == FRIENDCONN_STATUS_CONNECTING) {
|
||||||
if (friend_con->dht_pk_lastrecv + FRIEND_DHT_TIMEOUT < temp_time) {
|
if (friend_con->dht_pk_lastrecv + FRIEND_DHT_TIMEOUT < temp_time) {
|
||||||
if (friend_con->dht_lock > 0) {
|
if (friend_con->dht_lock_token > 0) {
|
||||||
dht_delfriend(fr_c->dht, friend_con->dht_temp_pk, friend_con->dht_lock);
|
dht_delfriend(fr_c->dht, friend_con->dht_temp_pk, friend_con->dht_lock_token);
|
||||||
friend_con->dht_lock = 0;
|
friend_con->dht_lock_token = 0;
|
||||||
memset(friend_con->dht_temp_pk, 0, CRYPTO_PUBLIC_KEY_SIZE);
|
memset(friend_con->dht_temp_pk, 0, CRYPTO_PUBLIC_KEY_SIZE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -978,7 +978,7 @@ void do_friend_connections(Friend_Connections *fr_c, void *userdata)
|
||||||
friend_con->dht_ip_port.ip.family = net_family_unspec();
|
friend_con->dht_ip_port.ip.family = net_family_unspec();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (friend_con->dht_lock > 0) {
|
if (friend_con->dht_lock_token > 0) {
|
||||||
if (friend_new_connection(fr_c, i) == 0) {
|
if (friend_new_connection(fr_c, i) == 0) {
|
||||||
set_direct_ip_port(fr_c->net_crypto, friend_con->crypt_connection_id, &friend_con->dht_ip_port, false);
|
set_direct_ip_port(fr_c->net_crypto, friend_con->crypt_connection_id, &friend_con->dht_ip_port, false);
|
||||||
connect_to_saved_tcp_relays(fr_c, i, MAX_FRIEND_TCP_CONNECTIONS / 2); /* Only fill it half up. */
|
connect_to_saved_tcp_relays(fr_c, i, MAX_FRIEND_TCP_CONNECTIONS / 2); /* Only fill it half up. */
|
||||||
|
|
Loading…
Reference in New Issue
Block a user