BUG #14722: Segfault in tuplesort_heap_siftup, 32 bit overflow

Started by Sergey Koposovalmost 9 years ago26 messagesbugs
Jump to latest
#1Sergey Koposov
skoposov@cmu.edu

The following bug has been logged on the website:

Bug reference: 14722
Logged by: Sergey Koposov
Email address: skoposov@cmu.edu
PostgreSQL version: 9.5.7
Operating system: Debian 7.11, x86_64
Description:

Hi,

I have a very large table (40e9 records) that I'm trying to create the index
on and I am getting a segmentation fault that could be traced as far as I
understand to a 32 bit int overflow in tuplesort_heap_siftup

Here are the commands leading to the crash:

wsdb=# set maintenance_work_mem to '70GB';

SET
wsdb=# create index on cgonzal.vvv_single_ks_sorted (q3c_ang2ipix(ra,dec));

----

Importantly the table has already been sorted by q3c_ang2ipix(ra,dec) !

--

Here is the table info:
wsdb=# explain select * from cgonzal.vvv_single_ks_sorted;
QUERY PLAN

---------------------------------------------------------------------------------------
Seq Scan on vvv_single_ks_sorted (cost=0.00..968967342.13 rows=43362626913
width=72)
(1 row)

wsdb=# \d cgonzal.vvv_single_ks_sorted
Table "cgonzal.vvv_single_ks_sorted"
Column | Type | Modifiers
---------+------------------+-----------
objid | bigint |
ra | double precision |
dec | double precision |
x | real |
y | real |
chip | integer |
mag | real |
e_mag | real |
class | integer |
frameid | bigint |
zp | double precision |
obj_id | bigint |

--------

Here is the gdb full stacktrace:
(gdb) bt full
#0 0x0000000000914cf8 in tuplesort_heap_siftup (state=0x23503f8,
checkIndex=1 '\001') at tuplesort.c:3014
j = -1879048193
memtuples = 0x7fb283aa1048
tuple = 0x7fba03aa0fd0
i = 1207959551
n = 1342177275
#1 0x000000000091430a in dumptuples (state=0x23503f8, alltuples=0 '\000')
at tuplesort.c:2648
__func__ = "dumptuples"
#2 0x00000000009120a3 in puttuple_common (state=0x23503f8,
tuple=0x7ffe420fefc0) at tuplesort.c:1468
__func__ = "puttuple_common"
#3 0x0000000000911d85 in tuplesort_putindextuplevalues (state=0x23503f8,
rel=0x7fd040f3b8e0, self=0x234ba34, values=0x7ffe420ff360,
isnull=0x7ffe420ff340 "") at tuplesort.c:1321
oldcontext = 0x23340b8
stup = {tuple = 0x7fbf040f6ae8, datum1 = 4710889527840951089,
isnull1 = 0 '\000', tupindex = 0}
original = 4710889527840951089
tuple = 0x7fbf040f6ae8
#4 0x00000000004d26dd in _bt_spool (btspool=0x234cba0, self=0x234ba34,
values=0x7ffe420ff360, isnull=0x7ffe420ff340 "") at nbtsort.c:192
No locals.
#5 0x00000000004cba67 in btbuildCallback (index=0x7fd040f3b8e0,
htup=0x234ba30, values=0x7ffe420ff360, isnull=0x7ffe420ff340 "",
tupleIsAlive=1 '\001', state=0x7ffe420ff550) at nbtree.c:179
buildstate = 0x7ffe420ff550
#6 0x0000000000525d8e in IndexBuildHeapRangeScan
(heapRelation=0x7fd040f32f78, indexRelation=0x7fd040f3b8e0,
indexInfo=0x2348308,
allow_sync=1 '\001', anyvisible=0 '\000', start_blockno=0,
numblocks=4294967295, callback=0x4cba0a <btbuildCallback>,
callback_state=0x7ffe420ff550) at index.c:2591
tupleIsAlive = 1 '\001'
is_system_catalog = 0 '\000'
checking_uniqueness = 0 '\000'
scan = 0x234b9e8
heapTuple = 0x234ba30
values = {4710889527840951089, 9472000, 36863416, 1089733344,
140730006762416, 9195433, 140730006762448, 140532419658520, 140730006762528,

140532419658464, 140730006762448, 9261444, 1976, 140532419658520,
4999282, 128, 36962306, 17179869199, 140730006762544, 9473335, 37029384,
37020152, 18288211008, 9498080, 37029368, 37020152,
140730006762592, 9478487, 140730006762624, 37029384, 64, 37020152}
isnull =
"\000\314\366@\320\177\000\000'Z\216\000\000\000\000\000\030\003\364@\320\177\000\000\310A3\002\000\000\000"
reltuples = 1342177279
predicate = 0x0
slot = 0x2348e08
estate = 0x2358448
---Type <return> to continue, or q <return> to quit---
econtext = 0x2358558
snapshot = 0xd366e0
OldestXmin = 1148880660
root_blkno = 16570089
root_offsets = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68,

69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 0 <repeats 210
times>}
__func__ = "IndexBuildHeapRangeScan"
#7 0x0000000000525556 in IndexBuildHeapScan (heapRelation=0x7fd040f32f78,
indexRelation=0x7fd040f3b8e0, indexInfo=0x2348308, allow_sync=1 '\001',
callback=0x4cba0a <btbuildCallback>, callback_state=0x7ffe420ff550) at
index.c:2162
No locals.
#8 0x00000000004cb979 in btbuild (fcinfo=0x7ffe420ff5d0) at nbtree.c:121
heap = 0x7fd040f32f78
index = 0x7fd040f3b8e0
indexInfo = 0x2348308
result = 0x234be28
reltuples = 6.9529861680561111e-310
buildstate = {isUnique = 0 '\000', haveDead = 0 '\000', heapRel =
0x7fd040f32f78, spool = 0x234cba0, spool2 = 0x0, indtuples = 1342177278}
__func__ = "btbuild"
#9 0x00000000008e8a13 in OidFunctionCall3Coll (functionId=338, collation=0,
arg1=140532419604344, arg2=140532419639520, arg3=36995848) at fmgr.c:1649
flinfo = {fn_addr = 0x4cb854 <btbuild>, fn_oid = 338, fn_nargs = 3,
fn_strict = 1 '\001', fn_retset = 0 '\000', fn_stats = 2 '\002',
fn_extra = 0x0, fn_mcxt = 0x23340b8, fn_expr = 0x0}
fcinfo = {flinfo = 0x7ffe420ff980, context = 0x0, resultinfo = 0x0,
fncollation = 0, isnull = 0 '\000', nargs = 3, arg = {140532419604344,
140532419639520, 36995848, 140532419656080, 68756505104, 128,
13, 17179869199, 140730006763184, 9472170, 128, 36023424, 140730006763152,

17189342519, 140532419627520, 36913336, 36023424, 37017440,
37017424, 36913336, 140730006763200, 512, 1108342496, 25769803839,
140730006763248, 9473335, 140532419653944, 36023424,
26878146304, 6912158, 140532419653928, 36023424, 140730006763296, 9478487,

140730006763296, 140532419653944, 0, 36023424, 140730006763328,
9230214, 672953898141726960, 140532419653944, 140730006763792, 9231509,
10999411261461, 140532419639520, 70458938492543, 156684292, 0,
18446744069414584320, 65536, 0, 140532419654472, 140532419654744,
672953910093598724, 16405, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
140532419656112, 140532419656056, 140532419657096, 37019880, 37027816,
37028344,
37028368, 37028392, 37028416, 37028584, 0, 0, 0, 0, 0, 0,
37028560, 0, 0, 0, 0, 36491160, 0, 0, 0, 13854912, 140532419640320,
8626848200,
36633224, 8589934592, 140730006763808, 6798261,
672953909936914436, 13854912},
argnull =
"\000\000\000\000\000\000\000\000\240h\323\000\000\000\000\000`\371\017B\376\177\000\000\032\274g\000\000\000\000\000\240h\323\000\
000\000\000\000\023\000\000\000\016\000\000\000\300\371\017B\376\177\000\000\aۍ",
'\000' <repeats 13 times>, "hD\224\000\000\000\000\000p<\224\000\000\
000\000\000\331\a\000\000\016\000\000\000\260\371\017B"}
result = 42949672962
__func__ = "OidFunctionCall3Coll"
#10 0x00000000005252a3 in index_build (heapRelation=0x7fd040f32f78,
indexRelation=0x7fd040f3b8e0, indexInfo=0x2348308, isprimary=0 '\000',
---Type <return> to continue, or q <return> to quit---
isreindex=0 '\000') at index.c:2025
procedure = 338
stats = 0x234cfec
save_userid = 10
save_sec_context = 0
save_nestlevel = 2
__func__ = "index_build"
#11 0x0000000000523f98 in index_create (heapRelation=0x7fd040f32f78,
indexRelationName=0x234b8e8 "vvv_single_ks_sorted_q3c_ang2ipix_idx",
indexRelationId=156684292, relFileNode=0, indexInfo=0x2348308,
indexColNames=0x234b638, accessMethodObjectId=403, tableSpaceId=0,
collationObjectId=0x234bdf8, classObjectId=0x234be10,
coloptions=0x234be28, reloptions=0, isprimary=0 '\000', isconstraint=0
'\000',
deferrable=0 '\000', initdeferred=0 '\000', allow_system_table_mods=0
'\000', skip_build=0 '\000', concurrent=0 '\000', is_internal=0 '\000',
if_not_exists=0 '\000') at index.c:1100
heapRelationId = 156673270
pg_class = 0x7fd040f81208
indexRelation = 0x7fd040f3b8e0
indexTupDesc = 0x23486c8
shared_relation = 0 '\000'
mapped_relation = 0 '\000'
is_exclusion = 0 '\000'
[120/270]
namespaceId = 16842
i = 1
relpersistence = 112 'p'
__func__ = "index_create"
#12 0x00000000005e9d27 in DefineIndex (relationId=156673270, stmt=0x23485f8,
indexRelationId=0, is_alter_table=0 '\000', check_rights=1 '\001',
skip_build=0 '\000', quiet=0 '\000') at indexcmds.c:607
indexRelationName = 0x234b8e8
"vvv_single_ks_sorted_q3c_ang2ipix_idx"
accessMethodName = 0x2348930 "btree"
typeObjectId = 0x234b780
collationObjectId = 0x234bdf8
classObjectId = 0x234be10
accessMethodId = 403
namespaceId = 16842
tablespaceId = 0
indexColNames = 0x234b638
rel = 0x7fd040f32f78
indexRelation = 0x23340b8
tuple = 0x7fd040f39b30
---Type <return> to continue, or q <return> to quit---
accessMethodForm = 0x7fd040f39ba8
amcanorder = 1 '\001'
amoptions = 2785
reloptions = 0
coloptions = 0x234be28
indexInfo = 0x2348308
numberOfAttributes = 1
limitXmin = 0
old_snapshots = 0x7fd040f32f78
address = {classId = 36997560, objectId = 0, objectSubId =
36995848}
n_old_snapshots = 0
heaprelid = {relId = 1108343952, dbId = 32766}
heaplocktag = {locktag_field1 = 4657712, locktag_field2 = 0,
locktag_field3 = 1108347536, locktag_field4 = 32766, locktag_type = 0
'\000',
locktag_lockmethodid = 0 '\000'}
lockmode = 5
snapshot = 0x2348308
i = 0
__func__ = "DefineIndex"
#13 0x00000000007ab5ec in ProcessUtilitySlow (parsetree=0x230c138,
queryString=0x230b268 "create index on cgonzal.vvv_single_ks_sorted
(q3c_ang2ipix(ra,dec));", context=PROCESS_UTILITY_TOPLEVEL, params=0x0,
dest=0x230c4d8, completionTag=0x7ffe42100420 "") at utility.c:1259
stmt = 0x23485f8
relid = 156673270
lockmode = 5
save_exception_stack = 0x7ffe421002e0
save_context_stack = 0x0
local_sigjmp_buf = {{__jmpbuf = {0, 8080871256505359237, 4657712,
140730006768272, 0, 0, 8080871325866564485, -8081285932728411259},
__mask_was_saved = 0, __saved_mask = {__val = {64, 36632424,
140730006765464, 140730006765472, 13829056, 8192, 36973152, 4657712, 5,
140730006765328, 9476353, 64, 0, 36973248, 13829056,
64}}}}
isTopLevel = 1 '\001'
isCompleteQuery = 1 '\001'
needCleanup = 0 '\000'
commandCollected = 0 '\000'
address = {classId = 0, objectId = 0, objectSubId = 13829056}
secondaryObject = {classId = 0, objectId = 0, objectSubId = 0}
__func__ = "ProcessUtilitySlow"
#14 0x00000000007aaa16 in standard_ProcessUtility (parsetree=0x230c138,
---Type <return> to continue, or q <return> to quit---
queryString=0x230b268 "create index on cgonzal.vvv_single_ks_sorted
(q3c_ang2ipix(ra,dec));", context=PROCESS_UTILITY_TOPLEVEL, params=0x0,
dest=0x230c4d8, completionTag=0x7ffe42100420 "") at utility.c:892
isTopLevel = 1 '\001'
__func__ = "standard_ProcessUtility"
#15 0x00000000007a9beb in ProcessUtility (parsetree=0x230c138,
queryString=0x230b268 "create index on cgonzal.vvv_single_ks_sorted
(q3c_ang2ipix(ra,dec));", context=PROCESS_UTILITY_TOPLEVEL, params=0x0,
dest=0x230c4d8, completionTag=0x7ffe42100420 "") at utility.c:334
No locals.
#16 0x00000000007a8e07 in PortalRunUtility (portal=0x2278798,
utilityStmt=0x230c138, isTopLevel=1 '\001', dest=0x230c4d8,
completionTag=0x7ffe42100420 "") at pquery.c:1183
active_snapshot_set = 1 '\001'
__func__ = "PortalRunUtility"
#17 0x00000000007a8fae in PortalRunMulti (portal=0x2278798, isTopLevel=1
'\001', dest=0x230c4d8, altdest=0x230c4d8,
completionTag=0x7ffe4210042[50/270]
at pquery.c:1314
stmt = 0x230c138
active_snapshot_set = 0 '\000'
stmtlist_item = 0x230c488
#18 0x00000000007a85c2 in PortalRun (portal=0x2278798,
count=9223372036854775807, isTopLevel=1 '\001', dest=0x230c4d8,
altdest=0x230c4d8,
completionTag=0x7ffe42100420 "") at pquery.c:812
save_exception_stack = 0x7ffe42100560
save_context_stack = 0x0
local_sigjmp_buf = {{__jmpbuf = {0, 8080871256352267141, 4657712,
140730006768272, 0, 0, 8080871256442444677, -8081285932000961659},
__mask_was_saved = 0, __saved_mask = {__val = {3432, 9356099,
36745776, 13, 0, 140730006766512, 9477730, 36624768, 88, 0, 36750640, 88,
9359107, 36750552, 36750640, 0}}}}
result = 0 '\000'
nprocessed = 32766
saveTopTransactionResourceOwner = 0x22ef878
saveTopTransactionContext = 0x22ef768
saveActivePortal = 0x0
saveResourceOwner = 0x22ef878
savePortalContext = 0x0
saveMemoryContext = 0x22ef768
__func__ = "PortalRun"
#19 0x00000000007a2ac3 in exec_simple_query (query_string=0x230b268 "create
index on cgonzal.vvv_single_ks_sorted (q3c_ang2ipix(ra,dec));")
[29/270]
at postgres.c:1104
parsetree = 0x230c138
portal = 0x2278798
---Type <return> to continue, or q <return> to quit---
snapshot_set = 0 '\000'
commandTag = 0xa4fc46 "CREATE INDEX"
completionTag =
"\000\004\020B\376\177\000\000\243b\217\000\000\000\000\000p\004\020B\376\177\000\000\000
\000\000D\000\000\000p\004\020B\376\1
77\000\000\252i\217\000\000\000\000\000\002\000\000\000\002\000\000\000J\000\000\000\000\000\000"
querytree_list = 0x230c458
plantree_list = 0x230c4a8
receiver = 0x230c4d8
format = 0
dest = DestRemote
oldcontext = 0x22ef768
parsetree_list = 0x230c1e8
parsetree_item = 0x230c1c8
save_log_statement_stats = 0 '\000'
was_logged = 0 '\000'
isTopLevel = 1 '\001'
msec_str =
"\260\004\020B\376\177\000\000\177:\217\000\000\000\000\000\006\000\000\000D\000\000\000h\262\060\002\000\000\000"
__func__ = "exec_simple_query"
#20 0x00000000007a69b2 in PostgresMain (argc=1, argv=0x225a220,
dbname=0x225a0d8 "wsdb", username=0x225a0b8 "postgres") at postgres.c:4051
query_string = 0x230b268 "create index on
cgonzal.vvv_single_ks_sorted (q3c_ang2ipix(ra,dec));"
firstchar = 81
input_message = {data = 0x230b268 "create index on
cgonzal.vvv_single_ks_sorted (q3c_ang2ipix(ra,dec));", len = 69, maxlen =
1024,
cursor = 69}
local_sigjmp_buf = {{__jmpbuf = {0, 8080871256293546885, 4657712,
140730006768272, 0, 0, 8080871256322907013, -8081285930984760443},
__mask_was_saved = 1, __saved_mask = {__val = {0, 36017624, 0,
0, 0, 0, 1024, 0, 30064771199, 140730006767152, 9473335, 36150008,
36017624, 30064771088, 36150008, 36149992}}}}
send_ready_for_query = 0 '\000'
__func__ = "PostgresMain"
#21 0x0000000000732973 in BackendRun (port=0x22a3050) at postmaster.c:4255
av = 0x225a220
maxac = 2
ac = 1
secs = 552065929
usecs = 554900
i = 1
__func__ = "BackendRun"
#22 0x0000000000732106 in BackendStartup (port=0x22a3050) at
postmaster.c:3929
bn = 0x22a3230
---Type <return> to continue, or q <return> to quit---
pid = 0
__func__ = "BackendStartup"
#23 0x000000000072ea84 in ServerLoop () at postmaster.c:1699
port = 0x22a3050
i = 4
rmask = {fds_bits = {128, 0 <repeats 15 times>}}
selres = 1
now = 1498750719
readmask = {fds_bits = {248, 0 <repeats 15 times>}}
nSockets = 8
last_lockfile_recheck_time = 1498750679
last_touch_time = 1498750679
__func__ = "ServerLoop"
#24 0x000000000072e100 in PostmasterMain (argc=3, argv=0x2259310) at
postmaster.c:1307
opt = -1
status = 0
userDoption = 0x227ad40 "/mnt/bigdata/pgdata9.5"
listen_addr_saved = 1 '\001'
i = 64
output_config_variable = 0x0
__func__ = "PostmasterMain"
#25 0x000000000068ecda in main (argc=3, argv=0x2259310) at main.c:228
do_check_root = 1 '\001'

----

From a quick look of the code it looks to me that the reason for the bug is
the 32 bit int overflow in the j=2*i+1 calculation inside the
tuplesort_heap_siftup leading to negative values of j.

Regards,
Sergey Koposov

--
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs

In reply to: Sergey Koposov (#1)
Re: BUG #14722: Segfault in tuplesort_heap_siftup, 32 bit overflow

On Thu, Jun 29, 2017 at 9:16 AM, <skoposov@cmu.edu> wrote:

I have a very large table (40e9 records) that I'm trying to create the index
on and I am getting a segmentation fault that could be traced as far as I
understand to a 32 bit int overflow in tuplesort_heap_siftup

Can your print the Tuplesortstate (the variable "state") within GDB,
and post it here?

--
Peter Geoghegan

--
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs

#3Sergey Koposov
skoposov@cmu.edu
In reply to: Peter Geoghegan (#2)
Re: BUG #14722: Segfault in tuplesort_heap_siftup, 32 bit overflow

On Thu, 2017-06-29 at 09:34 -0700, Peter Geoghegan wrote:

On Thu, Jun 29, 2017 at 9:16 AM, <skoposov@cmu.edu> wrote:

I have a very large table (40e9 records) that I'm trying to create the index
on and I am getting a segmentation fault that could be traced as far as I
understand to a 32 bit int overflow in tuplesort_heap_siftup

Can your print the Tuplesortstate (the variable "state") within GDB,
and post it here?

Here it is (it is a different run, as I closed the previous session,
but the bug is 100% reproduceable).

Program received signal SIGSEGV, Segmentation fault.
0x0000000000914cf8 in tuplesort_heap_siftup (state=0x234ffe8,
checkIndex=1 '\001') at tuplesort.c:3014
3014 HEAPCOMPARE(&memtuples[j], &memtuples[j
+ 1]) > 0)
(gdb) print (state)
$1 = (Tuplesortstate *) 0x234ffe8
(gdb) print (*state)
$2 = {status = TSS_BUILDRUNS, nKeys = 1, randomAccess = 0 '\000',
bounded = 0 '\000', boundUsed = 0 '\000', bound = 0, availMem =
-6442450776,
allowedMem = 75161927680, maxTapes = 262144, tapeRange = 262143,
sortcontext = 0x234e5c8, tapeset = 0x7fbf032a3048,
comparetup = 0x918d8a <comparetup_index_btree>, copytup = 0x919bbc
<copytup_index>, writetup = 0x91a243 <writetup_index>,
readtup = 0x91a31f <readtup_index>, memtuples = 0x7fb283aa1048,
memtupcount = 1342177275, memtupsize = 1342177279, growmemtuples = 0
'\000',
currentRun = 0, mergeactive = 0x7fc80aab9048 "", mergenext =
0x7fc80a23e048, mergelast = 0x7fc809ebc048, mergeavailslots =
0x7fc809dbb048,
mergeavailmem = 0x7fc8096ba048, mergefreelist = 0, mergefirstfree = 0,
Level = 1, destTape = 0, tp_fib = 0x7fc8095b9048, tp_runs =
0x7fc8094b8048,
tp_dummy = 0x7fc8093b7048, tp_tapenum = 0x7fc8081b7048, activeTapes =
0, result_tape = -1, current = 0, eof_reached = 0 '\000', markpos_block
= 0,
markpos_offset = 0, markpos_eof = 0 '\000', tupDesc = 0x0, sortKeys =
0x2350288, onlyKey = 0x0, abbrevNext = 10, indexInfo = 0x0, estate =
0x0,
heapRel = 0x7fd040f32f78, indexRel = 0x7fd040f3b8e0, enforceUnique = 0
'\000', hash_mask = 0, datumType = 0, datumTypeLen = 0,
datumTypeByVal = 0 '\000', ru_start = {tv = {tv_sec = 0, tv_usec = 0},
ru = {ru_utime = {tv_sec = 0, tv_usec = 0}, ru_stime = {tv_sec = 0,
tv_usec = 0}, ru_maxrss = 0, ru_ixrss = 0, ru_idrss = 0,
ru_isrss = 0, ru_minflt = 0, ru_majflt = 0, ru_nswap = 0, ru_inblock =
0,
ru_oublock = 0, ru_msgsnd = 0, ru_msgrcv = 0, ru_nsignals = 0,
ru_nvcsw = 0, ru_nivcsw = 0}}}

Regards,
Sergey

--
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs

In reply to: Sergey Koposov (#1)
Re: BUG #14722: Segfault in tuplesort_heap_siftup, 32 bit overflow

On Thu, Jun 29, 2017 at 9:16 AM, <skoposov@cmu.edu> wrote:

From a quick look of the code it looks to me that the reason for the bug is
the 32 bit int overflow in the j=2*i+1 calculation inside the
tuplesort_heap_siftup leading to negative values of j.

It seems likely that the explanation is as simple as that. This
happens during run generation with replacement selection. All versions
are affected, but version 9.6+ is dramatically less likely to be
affected, because replacement selection was all but killed in Postgres
9.6.

This is an oversight in commit 263865a. The fix is to use a variable
that won't overflow in tuplesort_heap_siftup() -- this is probably a
one-liner, because when the variable overflows today, the correct
behavior would be for control to break out of the loop that declares
the overflowing variable "j", and, I don't see any similar problem in
other heap maintenance routines. It's a very isolated problem.

I could write a patch.

--
Peter Geoghegan

--
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs

#5Sergey Koposov
skoposov@cmu.edu
In reply to: Peter Geoghegan (#4)
Re: BUG #14722: Segfault in tuplesort_heap_siftup, 32 bit overflow

On Thu, 2017-06-29 at 10:00 -0700, Peter Geoghegan wrote:

On Thu, Jun 29, 2017 at 9:16 AM, <skoposov@cmu.edu> wrote:

From a quick look of the code it looks to me that the reason for the bug is
the 32 bit int overflow in the j=2*i+1 calculation inside the
tuplesort_heap_siftup leading to negative values of j.

It seems likely that the explanation is as simple as that. This
happens during run generation with replacement selection. All versions
are affected, but version 9.6+ is dramatically less likely to be
affected, because replacement selection was all but killed in Postgres
9.6.

This is an oversight in commit 263865a. The fix is to use a variable
that won't overflow in tuplesort_heap_siftup() -- this is probably a
one-liner, because when the variable overflows today, the correct
behavior would be for control to break out of the loop that declares
the overflowing variable "j", and, I don't see any similar problem in
other heap maintenance routines. It's a very isolated problem.

I could write a patch.

For the time being I've just changed the type of i,j from int to long
(or int64) and I am running the index creation now. I let you submit a
patch -- thanks in advance.

I also noticed that the availMem variable was negative in the printout
of the TupleSortState.
availMem =-6442450776,
I don't know whether that's an issue on its own or was caused by the
(i,j) overflow. (availMem seems to be int64 variable though).

Sergey

--
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs

In reply to: Sergey Koposov (#5)
Re: BUG #14722: Segfault in tuplesort_heap_siftup, 32 bit overflow

On Thu, Jun 29, 2017 at 10:50 AM, Sergey Koposov <skoposov@cmu.edu> wrote:

For the time being I've just changed the type of i,j from int to long
(or int64) and I am running the index creation now. I let you submit a
patch -- thanks in advance.

That's more or less what I had in mind.

I also noticed that the availMem variable was negative in the printout
of the TupleSortState.
availMem =-6442450776,
I don't know whether that's an issue on its own or was caused by the
(i,j) overflow. (availMem seems to be int64 variable though).

That's definitely allowed to go negative, which is why it's int64.
That's about 6GB of memory, though, which seems unusually large.

--
Peter Geoghegan

--
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs

In reply to: Sergey Koposov (#1)
Re: BUG #14722: Segfault in tuplesort_heap_siftup, 32 bit overflow

On Thu, Jun 29, 2017 at 9:16 AM, <skoposov@cmu.edu> wrote:

Here are the commands leading to the crash:

wsdb=# set maintenance_work_mem to '70GB';

SET
wsdb=# create index on cgonzal.vvv_single_ks_sorted (q3c_ang2ipix(ra,dec));

----

Importantly the table has already been sorted by q3c_ang2ipix(ra,dec) !

BTW, given the exact details of what you're doing here, using only 1MB
of maintenance_work_mem will probably make the operation complete
sooner.

--
Peter Geoghegan

--
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs

#8Sergey Koposov
skoposov@cmu.edu
In reply to: Peter Geoghegan (#7)
Re: BUG #14722: Segfault in tuplesort_heap_siftup, 32 bit overflow

On Thu, 2017-06-29 at 11:59 -0700, Peter Geoghegan wrote:

On Thu, Jun 29, 2017 at 9:16 AM, <skoposov@cmu.edu> wrote:

Here are the commands leading to the crash:

wsdb=# set maintenance_work_mem to '70GB';

SET
wsdb=# create index on cgonzal.vvv_single_ks_sorted (q3c_ang2ipix(ra,dec));

----

Importantly the table has already been sorted by q3c_ang2ipix(ra,dec) !

BTW, given the exact details of what you're doing here, using only 1MB
of maintenance_work_mem will probably make the operation complete
sooner.

Thank you. It does seem to be ~ 50% faster.
It is somewhat unfortunate that maintenance_work_mem is
counterproductive in this scenario, given that indexing an ordered table
is a natural step in clustering a table...

Sergey

--
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs

#9Sergey Koposov
skoposov@cmu.edu
In reply to: Peter Geoghegan (#4)
Re: BUG #14722: Segfault in tuplesort_heap_siftup, 32 bit overflow

On Thu, 2017-06-29 at 10:00 -0700, Peter Geoghegan wrote:

On Thu, Jun 29, 2017 at 9:16 AM, <skoposov@cmu.edu> wrote:

From a quick look of the code it looks to me that the reason for the bug is
the 32 bit int overflow in the j=2*i+1 calculation inside the
tuplesort_heap_siftup leading to negative values of j.

It seems likely that the explanation is as simple as that. This
happens during run generation with replacement selection. All versions
are affected, but version 9.6+ is dramatically less likely to be
affected, because replacement selection was all but killed in Postgres
9.6.

This is an oversight in commit 263865a. The fix is to use a variable
that won't overflow in tuplesort_heap_siftup() -- this is probably a
one-liner, because when the variable overflows today, the correct
behavior would be for control to break out of the loop that declares
the overflowing variable "j", and, I don't see any similar problem in
other heap maintenance routines. It's a very isolated problem.

I could write a patch.

Just to avoid being forgotten, I attach a trivial patch against 9.5
branch as well as have created a commitfest submission
https://commitfest.postgresql.org/14/1189/

The script below allows to reproduce the bug (segfault) and test that
the patch fixes it: (>~70 GB of RAM are needed and 100+GB of disk
space)
---
create table xx3 as
select generate_series as a
from generate_series(0,(1.5*((1::bigint)<<31))::bigint);
set maintenance_work_mem to '70GB';
create index on xx3(a);
----

Hopefully somebody can take care of patching other PG branches.

Sergey

Attachments:

x.patchtext/x-patch; name=x.patchDownload+4-4
#10Tom Lane
tgl@sss.pgh.pa.us
In reply to: Sergey Koposov (#9)
Re: BUG #14722: Segfault in tuplesort_heap_siftup, 32 bit overflow

Sergey Koposov <skoposov@cmu.edu> writes:

On Thu, 2017-06-29 at 10:00 -0700, Peter Geoghegan wrote:

This is an oversight in commit 263865a. The fix is to use a variable
that won't overflow in tuplesort_heap_siftup() -- this is probably a
one-liner, because when the variable overflows today, the correct
behavior would be for control to break out of the loop that declares
the overflowing variable "j", and, I don't see any similar problem in
other heap maintenance routines. It's a very isolated problem.

I could write a patch.

Just to avoid being forgotten, I attach a trivial patch against 9.5
branch as well as have created a commitfest submission
https://commitfest.postgresql.org/14/1189/

I don't like s/int/int64/g as a fix for this. That loop is probably
a hot spot, and this fix is going to be expensive on any machine where
int64 isn't the native word width. How about something like this instead:

-		int			j = 2 * i + 1;
+		int			j;
+		if (unlikely(i > INT_MAX / 2))
+			break;		/* if j would overflow, we're done */
+		j = 2 * i + 1;
		if (j >= n)
			break;

regards, tom lane

--
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs

#11Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#10)
Re: BUG #14722: Segfault in tuplesort_heap_siftup, 32 bit overflow

On 2017-07-05 18:03:56 -0400, Tom Lane wrote:

Sergey Koposov <skoposov@cmu.edu> writes:

On Thu, 2017-06-29 at 10:00 -0700, Peter Geoghegan wrote:

This is an oversight in commit 263865a. The fix is to use a variable
that won't overflow in tuplesort_heap_siftup() -- this is probably a
one-liner, because when the variable overflows today, the correct
behavior would be for control to break out of the loop that declares
the overflowing variable "j", and, I don't see any similar problem in
other heap maintenance routines. It's a very isolated problem.

I could write a patch.

Just to avoid being forgotten, I attach a trivial patch against 9.5
branch as well as have created a commitfest submission
https://commitfest.postgresql.org/14/1189/

I don't like s/int/int64/g as a fix for this. That loop is probably
a hot spot, and this fix is going to be expensive on any machine where
int64 isn't the native word width. How about something like this instead:

-		int			j = 2 * i + 1;
+		int			j;
+		if (unlikely(i > INT_MAX / 2))
+			break;		/* if j would overflow, we're done */
+		j = 2 * i + 1;
if (j >= n)
break;

Isn't an added conditional likely going to be more costly than the
s/32/64/ bit calculations on the majority of machines pg runs on? I'm
quite doubtful that it's worth catering for the few cases where that's
really slow.

- Andres

--
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs

In reply to: Andres Freund (#11)
Re: BUG #14722: Segfault in tuplesort_heap_siftup, 32 bit overflow

On Wed, Jul 5, 2017 at 3:14 PM, Andres Freund <andres@anarazel.de> wrote:

Isn't an added conditional likely going to be more costly than the
s/32/64/ bit calculations on the majority of machines pg runs on? I'm
quite doubtful that it's worth catering for the few cases where that's
really slow.

I doubt that myself. Especially prior to Postgres 10, where merging
will have tuplesort_heap_insert() as the bottleneck.

In Postgres 10, tuplesort external sort run merging became much faster
following commit 24598337c8d. It might be noticeable if such a machine
were using Postgres 10 already, and really had something to lose, but
that seems very unlikely.

--
Peter Geoghegan

--
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs

In reply to: Peter Geoghegan (#12)
Re: BUG #14722: Segfault in tuplesort_heap_siftup, 32 bit overflow

On Wed, Jul 5, 2017 at 3:19 PM, Peter Geoghegan <pg@bowt.ie> wrote:

I doubt that myself. Especially prior to Postgres 10, where merging
will have tuplesort_heap_insert() as the bottleneck.

BTW, I do of course understand that the reporter isn't talking about
merging, but rather is talking about run generation using replacement
selection. Replacement selection is more or less obsolete, and was
more or less removed in Postgres 9.6. I don't think that its needs
should be given much weight here; this is mostly about merging.

I wanted to make merging use its own dedicated function within the
patch that became 24598337c8d, and leave tuplesort_heap_insert() and
tuplesort_heap_siftup() as things used only by replacement selection +
Top-N heap sort, but Heikki overruled me on that. He might have been
right about that. I'm not sure, and never took the time to follow up
with it.

tuplesort_heap_replace_top(), which is where this logic lives now that
tuplesort_heap_siftup() was broken up, certainly is a red-hot code
path where individual instructions could matter. I've looked at the
disassembly of the function in the past, which is rare for me.

--
Peter Geoghegan

--
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs

#14Heikki Linnakangas
heikki.linnakangas@enterprisedb.com
In reply to: Andres Freund (#11)
Re: BUG #14722: Segfault in tuplesort_heap_siftup, 32 bit overflow

On 07/06/2017 01:14 AM, Andres Freund wrote:

On 2017-07-05 18:03:56 -0400, Tom Lane wrote:

I don't like s/int/int64/g as a fix for this. That loop is probably
a hot spot, and this fix is going to be expensive on any machine where
int64 isn't the native word width. How about something like this instead:

-		int			j = 2 * i + 1;
+		int			j;
+		if (unlikely(i > INT_MAX / 2))
+			break;		/* if j would overflow, we're done */
+		j = 2 * i + 1;
if (j >= n)
break;

Isn't an added conditional likely going to be more costly than the
s/32/64/ bit calculations on the majority of machines pg runs on? I'm
quite doubtful that it's worth catering for the few cases where that's
really slow.

Another option to use "unsigned int", on the assumption that UINT_MAX >=
INT_MAX * 2 + 1. And to eliminate that assumption, we can use (UINT_MAX
- 1) / 2 as the maximum size of the memtuples array, rather than INT_MAX.

- Heikki

--
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs

#15Tom Lane
tgl@sss.pgh.pa.us
In reply to: Heikki Linnakangas (#14)
Re: BUG #14722: Segfault in tuplesort_heap_siftup, 32 bit overflow

Heikki Linnakangas <hlinnaka@iki.fi> writes:

On 07/06/2017 01:14 AM, Andres Freund wrote:

On 2017-07-05 18:03:56 -0400, Tom Lane wrote:

I don't like s/int/int64/g as a fix for this. That loop is probably
a hot spot, and this fix is going to be expensive on any machine where
int64 isn't the native word width. How about something like this instead:

Another option to use "unsigned int", on the assumption that UINT_MAX >=
INT_MAX * 2 + 1.

Ah, that seems like a fine idea.

And to eliminate that assumption, we can use (UINT_MAX
- 1) / 2 as the maximum size of the memtuples array, rather than INT_MAX.

Uh ... what assumption? That's certainly true on any twos-complement
machine. Besides, if you're worried about hypothetical portability
issues, I'm not sure it's any better to assume that (UINT_MAX - 1) / 2
fits in a signed int.

regards, tom lane

--
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs

In reply to: Tom Lane (#15)
Re: BUG #14722: Segfault in tuplesort_heap_siftup, 32 bit overflow

On Wed, Jul 12, 2017 at 8:42 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Another option to use "unsigned int", on the assumption that UINT_MAX >=
INT_MAX * 2 + 1.

Ah, that seems like a fine idea.

Works for me.

--
Peter Geoghegan

--
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs

#17Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Geoghegan (#16)
Re: BUG #14722: Segfault in tuplesort_heap_siftup, 32 bit overflow

Peter Geoghegan <pg@bowt.ie> writes:

On Wed, Jul 12, 2017 at 8:42 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Another option to use "unsigned int", on the assumption that UINT_MAX >=
INT_MAX * 2 + 1.

Ah, that seems like a fine idea.

Works for me.

I'll go make it so, unless Heikki's already on it?

regards, tom lane

--
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs

#18Heikki Linnakangas
heikki.linnakangas@enterprisedb.com
In reply to: Tom Lane (#17)
Re: BUG #14722: Segfault in tuplesort_heap_siftup, 32 bit overflow

On 07/12/2017 07:14 PM, Tom Lane wrote:

Peter Geoghegan <pg@bowt.ie> writes:

On Wed, Jul 12, 2017 at 8:42 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Another option to use "unsigned int", on the assumption that UINT_MAX >=
INT_MAX * 2 + 1.

Ah, that seems like a fine idea.

Works for me.

I'll go make it so, unless Heikki's already on it?

I'm not. Thanks!

And to eliminate that assumption, we can use (UINT_MAX
- 1) / 2 as the maximum size of the memtuples array, rather than INT_MAX.

Uh ... what assumption? That's certainly true on any twos-complement
machine. Besides, if you're worried about hypothetical portability
issues, ...

Right, it's a hypothetical portability issue. The assumption we're
making is that UINT_MAX >= INT_MAX * 2 + 1. I'm not aware of any system
where it's not true, but I don't know what the C standards say about that.

... I'm not sure it's any better to assume that (UINT_MAX - 1) / 2
fits in a signed int.

Well, you could do Min(INT_MAX, (UINT_MAX - 1 / 2). Or just add a
StaticAssertion for it. Or just note in a comment that we're making that
assumption.

- Heikki

--
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs

#19Tom Lane
tgl@sss.pgh.pa.us
In reply to: Heikki Linnakangas (#18)
Re: BUG #14722: Segfault in tuplesort_heap_siftup, 32 bit overflow

Heikki Linnakangas <hlinnaka@iki.fi> writes:

On 07/12/2017 07:14 PM, Tom Lane wrote:

Uh ... what assumption? That's certainly true on any twos-complement
machine. Besides, if you're worried about hypothetical portability
issues, ...

Right, it's a hypothetical portability issue. The assumption we're
making is that UINT_MAX >= INT_MAX * 2 + 1. I'm not aware of any system
where it's not true, but I don't know what the C standards say about that.

Actually, at the top of the loop we have i < n <= INT_MAX, so the
assumption only needs to be UINT_MAX >= INT_MAX * 2 - 1.

My copy of the C99 draft says, among other things

6.2.5 Types

[#6] For each of the signed integer types, there is a
corresponding (but different) unsigned integer type
(designated with the keyword unsigned) that uses the same
amount of storage (including sign information) and has the
same alignment requirements.

[#9] The range of nonnegative values of a signed integer
type is a subrange of the corresponding unsigned integer
type, and the representation of the same value in each type
is the same.

6.2.6.2 Integer types

[#1] For unsigned integer types other than unsigned char,
the bits of the object representation shall be divided into
two groups: value bits and padding bits (there need not be
any of the latter). If there are N value bits, each bit
shall represent a different power of 2 between 1 and 2N-1,
so that objects of that type shall be capable of
representing values from 0 to 2N-1 using a pure binary
representation; this shall be known as the value
representation. The values of any padding bits are
unspecified.37)

[#2] For signed integer types, the bits of the object
representation shall be divided into three groups: value
bits, padding bits, and the sign bit. There need not be any
padding bits; there shall be exactly one sign bit. Each bit
that is a value bit shall have the same value as the same
bit in the object representation of the corresponding
unsigned type (if there are M value bits in the signed type
and N in the unsigned type, then M<=N). If the sign bit is
zero, it shall not affect the resulting value. If the sign
bit is one, then the value shall be modified in one of the
following ways:

-- the corresponding value with sign bit 0 is negated;

-- the sign bit has the value -2N;

-- the sign bit has the value 1-2N.

[#3] The values of any padding bits are unspecified.

Both 6.2.5.9 and 6.2.6.2.2 constrain INT_MAX to be <= UINT_MAX.
In principle, you could have a conforming implementation in which
they were the same, and the sign bit of a signed integer was treated
as a useless padding bit in an unsigned integer. I can't imagine
anyone actually building it that way though; what would be the point?
But as long as there aren't wasted bits in an unsigned int, these
rules require INT_MAX to be <= UINT_MAX/2, AFAICS.

regards, tom lane

--
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs

In reply to: Heikki Linnakangas (#18)
Re: BUG #14722: Segfault in tuplesort_heap_siftup, 32 bit overflow

On Wed, Jul 12, 2017 at 9:20 AM, Heikki Linnakangas <hlinnaka@iki.fi> wrote:

Uh ... what assumption? That's certainly true on any twos-complement
machine. Besides, if you're worried about hypothetical portability
issues, ...

Right, it's a hypothetical portability issue. The assumption we're making is
that UINT_MAX >= INT_MAX * 2 + 1. I'm not aware of any system where it's not
true, but I don't know what the C standards say about that.

Intuitively, it seems very likely to be true, since two's complement
arithmetic is already assumed by Postgres, and addition and
multiplication work the same way at the instruction level for unsigned
and signed (two's complement) integers. In order for this to break,
int and unsigned int would have to have different widths.

Well, you could do Min(INT_MAX, (UINT_MAX - 1 / 2). Or just add a
StaticAssertion for it. Or just note in a comment that we're making that
assumption.

I like the idea of a StaticAssertion().

--
Peter Geoghegan

--
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs

#21Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Geoghegan (#20)
In reply to: Heikki Linnakangas (#14)
#23Heikki Linnakangas
heikki.linnakangas@enterprisedb.com
In reply to: Peter Geoghegan (#22)
In reply to: Heikki Linnakangas (#23)
#25daveg
daveg@sonic.net
In reply to: Tom Lane (#21)
#26Tom Lane
tgl@sss.pgh.pa.us
In reply to: daveg (#25)