You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
System Info:
OS: Fedora 27, 64-Bit uname -r: 4.15.15-300.fc27.x86_64
gdb version: GNU gdb (GDB) Fedora 8.0.1-36.fc27
glibc 2.26
Analysis
Running the tests program with gdb (gdb ./tests) yields the following stack trace:
#0 btc_btree_tdestroy (freekey=<optimized out>, root=0xde4350)
at ./include/btc/utils.h:74
#1 btc_wallet_free (wallet=0xde5570) at src/wallet.c:283
#2 0x00000000004093c8 in test_wallet_basics () at test/wallet_tests.c:204
#3 0x0000000000401ce6 in main () at test/unittester.c:118
After experimenting a bit, I found that after the insertion performed by tsearch, the left node pointer is 0x1, unlike the right one, which is 0x0. Breaking at src/wallet.c:537:
Breakpoint 1, btc_wallet_next_addr (wallet=wallet@entry=0xde5570)
at src/wallet.c:537
537 btc_wallet_addr* checknode = tsearch(waddr, &wallet->waddr_rbtree, btc_wallet_addr_compare);
(gdb) p wallet->waddr_rbtree
$1 = (void *) 0x0
(gdb) n
538 vector_add(wallet->waddr_vector, waddr);
(gdb) p wallet->waddr_rbtree
$2 = (void *) 0xde4350
(gdb) p ((struct btc_btree_node *) wallet->waddr_rbtree)->left
$3 = (struct btc_btree_node *) 0x1
(gdb) p ((struct btc_btree_node *) wallet->waddr_rbtree)->right
$4 = (struct btc_btree_node *) 0x0
(gdb)
This is because, as explained in a comment in misc/tsearch.c, if malloc returns aligned addresses, the lower bits of the left node's address are zero anyway and can be used to store whether the node is a red or a black one in the LSB. The definition of SETLEFT, the macro used to set the left node's value, is defined as:
In btc_btree_tdestroy:74 in file include/btc/utils.h the tree is destroyed recursively, but the base case is r == 0, so if the node from the red-black tree is red, but has no children, this check might fail and the function could end up destroying a pointer with value 0x1, which has undefined behavior and might result in a segmentation fault, as it does in my case.
Solution
The easy fix is to check for r == 0 || (uintptr_t) r == 1 instead of just r == 0 and clearing the LSB of r afterwards, but then you rely on the implementation of tsearch. I would not write it like that.
(r == NULL seems cleaner to me anyway.)
I just discovered your comments:
/* support substitude for GNU only tdestroy */
/* Let's hope the node struct is always compatible */
With this patch, they introduced a compile-time switch, which, if possible, includes the color bit in the left node, rendering the r == 0 insufficient. If the compiler determines that this is not possible, because malloc returns unaligned addresses, a separate struct member to indicate whether the node is red or black. Either way, the code is not well-defined: either the check fails and it ends up trying to destroy the address 0x1, which is undefined, or your struct is not compatible because of the additional red/black flag, also rendering the code undefined.
I would suggest using tdestroy anyway, which is part of the proper interface. It is a GNU extension, but you use -std=gnu99 anyway.
However, the cleanest way would be to quit using tsearch and friends and rolling your own red-black tree implementation entirely. tsearch is only supposed to be, and can, in a clean, portable way, only be used together with the other functions of the <search.h> interface and if you opt against using the other functions, it does not make sense to use tsearch either, I think.
The text was updated successfully, but these errors were encountered:
Bug Report
I built the
wallet_new
branch with./autogen.sh && ./configure --enable-debug && make check
.Running tests with
./tests
:test-suite.log
:System Info:
OS: Fedora 27, 64-Bit
uname -r
: 4.15.15-300.fc27.x86_64gdb version: GNU gdb (GDB) Fedora 8.0.1-36.fc27
glibc 2.26
Analysis
Running the
tests
program withgdb
(gdb ./tests
) yields the following stack trace:After experimenting a bit, I found that after the insertion performed by
tsearch
, the left node pointer is0x1
, unlike the right one, which is0x0
. Breaking atsrc/wallet.c:537
:This is because, as explained in a comment in
misc/tsearch.c
, ifmalloc
returns aligned addresses, the lower bits of the left node's address are zero anyway and can be used to store whether the node is a red or a black one in the LSB. The definition ofSETLEFT
, the macro used to set the left node's value, is defined as:In
btc_btree_tdestroy:74
in fileinclude/btc/utils.h
the tree is destroyed recursively, but the base case isr == 0
, so if the node from the red-black tree is red, but has no children, this check might fail and the function could end up destroying a pointer with value0x1
, which has undefined behavior and might result in a segmentation fault, as it does in my case.Solution
The easy fix is to check for
r == 0 || (uintptr_t) r == 1
instead of justr == 0
and clearing the LSB ofr
afterwards, but then you rely on the implementation oftsearch
. I would not write it like that.(
r == NULL
seems cleaner to me anyway.)I just discovered your comments:
With this patch, they introduced a compile-time switch, which, if possible, includes the color bit in the left node, rendering the
r == 0
insufficient. If the compiler determines that this is not possible, becausemalloc
returns unaligned addresses, a separatestruct
member to indicate whether the node is red or black. Either way, the code is not well-defined: either the check fails and it ends up trying to destroy the address0x1
, which is undefined, or yourstruct
is not compatible because of the additional red/black flag, also rendering the code undefined.I would suggest using
tdestroy
anyway, which is part of the proper interface. It is a GNU extension, but you use-std=gnu99
anyway.However, the cleanest way would be to quit using
tsearch
and friends and rolling your own red-black tree implementation entirely.tsearch
is only supposed to be, and can, in a clean, portable way, only be used together with the other functions of the<search.h>
interface and if you opt against using the other functions, it does not make sense to usetsearch
either, I think.The text was updated successfully, but these errors were encountered: