Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Ensure Soundex difference() function handles empty input sanely.
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 16 May 2023 14:53:42 +0000 (10:53 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 16 May 2023 14:53:42 +0000 (10:53 -0400)
fuzzystrmatch's difference() function assumes that _soundex()
always initializes its output buffer fully.  This was not so for
the case of a string containing no alphabetic characters, resulting
in unstable output and Valgrind complaints.

Fix by using memset() to fill the whole buffer in the early-exit
case.  Also make some cosmetic improvements (I didn't care for the
random switches between "instr[0]" and "*instr" notation).

Report and diagnosis by Alexander Lakhin (bug #17935).
Back-patch to all supported branches.

Discussion: https://postgr.es/m/17935-b99316aa79c18513@postgresql.org

contrib/fuzzystrmatch/expected/fuzzystrmatch.out
contrib/fuzzystrmatch/fuzzystrmatch.c
contrib/fuzzystrmatch/sql/fuzzystrmatch.sql

index 493c95cdfa54730bcc11c4710098c6fcb6e9b0d5..2827e81eb47eb13c29cc4e45d0d63f63ca79e501 100644 (file)
@@ -23,6 +23,12 @@ SELECT soundex('Anne'), soundex('Margaret'), difference('Anne', 'Margaret');
  A500    | M626    |          0
 (1 row)
 
+SELECT soundex(''), difference('', '');
+ soundex | difference 
+---------+------------
+         |          4
+(1 row)
+
 SELECT levenshtein('GUMBO', 'GAMBOL');
  levenshtein 
 -------------
index b8992f7c3cb7a1f4472fed9ac2996a8a2442e6cd..8d3420f6740294cbaa54d848c7490e05294037f1 100644 (file)
@@ -729,16 +729,14 @@ _soundex(const char *instr, char *outstr)
    AssertArg(instr);
    AssertArg(outstr);
 
-   outstr[SOUNDEX_LEN] = '\0';
-
    /* Skip leading non-alphabetic characters */
-   while (!isalpha((unsigned char) instr[0]) && instr[0])
+   while (*instr && !isalpha((unsigned char) *instr))
        ++instr;
 
-   /* No string left */
-   if (!instr[0])
+   /* If no string left, return all-zeroes buffer */
+   if (!*instr)
    {
-       outstr[0] = (char) 0;
+       memset(outstr, '\0', SOUNDEX_LEN + 1);
        return;
    }
 
@@ -751,7 +749,7 @@ _soundex(const char *instr, char *outstr)
        if (isalpha((unsigned char) *instr) &&
            soundex_code(*instr) != soundex_code(*(instr - 1)))
        {
-           *outstr = soundex_code(instr[0]);
+           *outstr = soundex_code(*instr);
            if (*outstr != '0')
            {
                ++outstr;
@@ -768,6 +766,9 @@ _soundex(const char *instr, char *outstr)
        ++outstr;
        ++count;
    }
+
+   /* And null-terminate */
+   *outstr = '\0';
 }
 
 PG_FUNCTION_INFO_V1(difference);
index f05dc28ffb1ac7b5cebaa5b7df34ee6228ef8666..1d0e2197fbe6240bcf94235ecfb8fa986d912afc 100644 (file)
@@ -6,6 +6,7 @@ SELECT soundex('hello world!');
 SELECT soundex('Anne'), soundex('Ann'), difference('Anne', 'Ann');
 SELECT soundex('Anne'), soundex('Andrew'), difference('Anne', 'Andrew');
 SELECT soundex('Anne'), soundex('Margaret'), difference('Anne', 'Margaret');
+SELECT soundex(''), difference('', '');
 
 
 SELECT levenshtein('GUMBO', 'GAMBOL');