Skip to content

Commit 701c881

Browse files
committed
Fix contrib/seg to be more wary of long input numbers.
seg stores the number of significant digits in an input number in a "char" field. If char is signed, and the input is more than 127 digits long, the count can read out as negative causing seg_out() to print garbage (or, if you're really unlucky, even crash). To fix, clamp the digit count to be not more than FLT_DIG. (In theory this loses some information about what the original input was, but it doesn't seem like useful information; it would not survive dump/restore in any case.) Also, in case there are stored values of the seg type containing bad data, add a clamp in seg_out's restore() subroutine. Per bug #17725 from Robins Tharakan. It's been like this forever, so back-patch to all supported branches. Discussion: https://postgr.es/m/17725-0a09313b67fbe86e@postgresql.org
1 parent 33dd895 commit 701c881

File tree

4 files changed

+33
-7
lines changed

4 files changed

+33
-7
lines changed

contrib/seg/expected/seg.out

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,13 @@ SELECT '12.34567890123456'::seg AS seg;
256256
12.3457
257257
(1 row)
258258

259+
-- Same, with a very long input
260+
SELECT '12.3456789012345600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'::seg AS seg;
261+
seg
262+
---------
263+
12.3457
264+
(1 row)
265+
259266
-- Numbers with certainty indicators
260267
SELECT '~6.5'::seg AS seg;
261268
seg

contrib/seg/seg.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -928,9 +928,13 @@ restore(char *result, float val, int n)
928928

929929
/*
930930
* Put a cap on the number of significant digits to avoid garbage in the
931-
* output and ensure we don't overrun the result buffer.
931+
* output and ensure we don't overrun the result buffer. (n should not be
932+
* negative, but check to protect ourselves against corrupted data.)
932933
*/
933-
n = Min(n, FLT_DIG);
934+
if (n <= 0)
935+
n = FLT_DIG;
936+
else
937+
n = Min(n, FLT_DIG);
934938

935939
/* remember the sign */
936940
sign = (val < 0 ? 1 : 0);

contrib/seg/segparse.y

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include "postgres.h"
55

6+
#include <float.h>
67
#include <math.h>
78

89
#include "fmgr.h"
@@ -20,6 +21,8 @@
2021

2122
static float seg_atof(const char *value);
2223

24+
static int sig_digits(const char *value);
25+
2326
static char strbuf[25] = {
2427
'0', '0', '0', '0', '0',
2528
'0', '0', '0', '0', '0',
@@ -62,9 +65,9 @@ range: boundary PLUMIN deviation
6265
result->lower = $1.val - $3.val;
6366
result->upper = $1.val + $3.val;
6467
sprintf(strbuf, "%g", result->lower);
65-
result->l_sigd = Max(Min(6, significant_digits(strbuf)), Max($1.sigd, $3.sigd));
68+
result->l_sigd = Max(sig_digits(strbuf), Max($1.sigd, $3.sigd));
6669
sprintf(strbuf, "%g", result->upper);
67-
result->u_sigd = Max(Min(6, significant_digits(strbuf)), Max($1.sigd, $3.sigd));
70+
result->u_sigd = Max(sig_digits(strbuf), Max($1.sigd, $3.sigd));
6871
result->l_ext = '\0';
6972
result->u_ext = '\0';
7073
}
@@ -121,7 +124,7 @@ boundary: SEGFLOAT
121124
float val = seg_atof($1);
122125

123126
$$.ext = '\0';
124-
$$.sigd = significant_digits($1);
127+
$$.sigd = sig_digits($1);
125128
$$.val = val;
126129
}
127130
| EXTENSION SEGFLOAT
@@ -130,7 +133,7 @@ boundary: SEGFLOAT
130133
float val = seg_atof($2);
131134

132135
$$.ext = $1[0];
133-
$$.sigd = significant_digits($2);
136+
$$.sigd = sig_digits($2);
134137
$$.val = val;
135138
}
136139
;
@@ -141,7 +144,7 @@ deviation: SEGFLOAT
141144
float val = seg_atof($1);
142145

143146
$$.ext = '\0';
144-
$$.sigd = significant_digits($1);
147+
$$.sigd = sig_digits($1);
145148
$$.val = val;
146149
}
147150
;
@@ -157,3 +160,12 @@ seg_atof(const char *value)
157160
datum = DirectFunctionCall1(float4in, CStringGetDatum(value));
158161
return DatumGetFloat4(datum);
159162
}
163+
164+
static int
165+
sig_digits(const char *value)
166+
{
167+
int n = significant_digits(value);
168+
169+
/* Clamp, to ensure value will fit in sigd fields */
170+
return Min(n, FLT_DIG);
171+
}

contrib/seg/sql/seg.sql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ SELECT '3.400e5'::seg AS seg;
6060
-- Digits truncated
6161
SELECT '12.34567890123456'::seg AS seg;
6262

63+
-- Same, with a very long input
64+
SELECT '12.3456789012345600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'::seg AS seg;
65+
6366
-- Numbers with certainty indicators
6467
SELECT '~6.5'::seg AS seg;
6568
SELECT '<6.5'::seg AS seg;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy