]> granicus.if.org Git - postgresql/commitdiff
Fix several one-byte buffer over-reads in to_number
authorPeter Eisentraut <peter_e@gmx.net>
Mon, 8 Aug 2016 15:12:59 +0000 (11:12 -0400)
committerPeter Eisentraut <peter_e@gmx.net>
Mon, 8 Aug 2016 15:12:59 +0000 (11:12 -0400)
Several places in NUM_numpart_from_char(), which is called from the SQL
function to_number(text, text), could accidentally read one byte past
the end of the input buffer (which comes from the input text datum and
is not null-terminated).

1. One leading space character would be skipped, but there was no check
   that the input was at least one byte long.  This does not happen in
   practice, but for defensiveness, add a check anyway.

2. Commit 4a3a1e2cf apparently accidentally doubled that code that skips
   one space character (so that two spaces might be skipped), but there
   was no overflow check before skipping the second byte.  Fix by
   removing that duplicate code.

3. A logic error would allow a one-byte over-read when looking for a
   trailing sign (S) placeholder.

In each case, the extra byte cannot be read out directly, but looking at
it might cause a crash.

The third item was discovered by Piotr Stefaniak, the first two were
found and analyzed by Tom Lane and Peter Eisentraut.

src/backend/utils/adt/formatting.c

index ae93fe01679cf1a92cb0f9aca166f5799d859c01..bbd97dc84bba1ed53f35f7fbf6052a4ac6279643 100644 (file)
@@ -4188,12 +4188,12 @@ NUM_numpart_from_char(NUMProc *Np, int id, int input_len)
                 (id == NUM_0 || id == NUM_9) ? "NUM_0/9" : id == NUM_DEC ? "NUM_DEC" : "???");
 #endif
 
-       if (*Np->inout_p == ' ')
-               Np->inout_p++;
-
 #define OVERLOAD_TEST  (Np->inout_p >= Np->inout + input_len)
 #define AMOUNT_TEST(_s) (input_len-(Np->inout_p-Np->inout) >= _s)
 
+       if (OVERLOAD_TEST)
+               return;
+
        if (*Np->inout_p == ' ')
                Np->inout_p++;
 
@@ -4331,7 +4331,7 @@ NUM_numpart_from_char(NUMProc *Np, int id, int input_len)
                 * next char is not digit
                 */
                if (IS_LSIGN(Np->Num) && isread &&
-                       (Np->inout_p + 1) <= Np->inout + input_len &&
+                       (Np->inout_p + 1) < Np->inout + input_len &&
                        !isdigit((unsigned char) *(Np->inout_p + 1)))
                {
                        int                     x;