Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 82f97d3

Browse files
committed
Code review for psql's helpSQL() function.
The loops to identify word boundaries could access past the end of the input string. Likely that would never result in an actual crash, but it makes valgrind unhappy. The logic to try different numbers of words didn't work when the input has two words but we only have a match to the first, eg "\h with select". (We must "continue" the pass loop, not "break".) The logic to compute nl_count was bizarrely managed, and in at least two code paths could end up calling PageOutput with nl_count = 0, resulting in failing to paginate output that should have been fed to the pager. Also, in v12 and up, the nl_count calculation hadn't been updated to account for the addition of a URL. The PQExpBuffer holding the command syntax details wasn't freed, resulting in a session-lifespan memory leak. While here, improve some comments, choose a more descriptive name for a variable, fix inconsistent datatype choice for another variable. Per bug #16837 from Alexander Lakhin. This code is very old, so back-patch to all supported branches. Kyotaro Horiguchi and Tom Lane Discussion: https://postgr.es/m/16837-479bcd56040c71b3@postgresql.org
1 parent 820aa9e commit 82f97d3

File tree

1 file changed

+37
-22
lines changed

1 file changed

+37
-22
lines changed

src/bin/psql/help.c

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,7 @@ helpSQL(const char *topic, unsigned short int pager)
528528
int i;
529529
int j;
530530

531+
/* Find screen width to determine how many columns will fit */
531532
#ifdef TIOCGWINSZ
532533
struct winsize screen_size;
533534

@@ -565,56 +566,63 @@ helpSQL(const char *topic, unsigned short int pager)
565566
else
566567
{
567568
int i,
568-
j,
569-
x = 0;
570-
bool help_found = false;
569+
pass;
571570
FILE *output = NULL;
572571
size_t len,
573-
wordlen;
574-
int nl_count = 0;
572+
wordlen,
573+
j;
574+
int nl_count;
575575

576576
/*
577+
* len is the amount of the input to compare to the help topic names.
577578
* We first try exact match, then first + second words, then first
578579
* word only.
579580
*/
580581
len = strlen(topic);
581582

582-
for (x = 1; x <= 3; x++)
583+
for (pass = 1; pass <= 3; pass++)
583584
{
584-
if (x > 1) /* Nothing on first pass - try the opening
585+
if (pass > 1) /* Nothing on first pass - try the opening
585586
* word(s) */
586587
{
587588
wordlen = j = 1;
588-
while (topic[j] != ' ' && j++ < len)
589+
while (j < len && topic[j++] != ' ')
589590
wordlen++;
590-
if (x == 2)
591+
if (pass == 2 && j < len)
591592
{
592-
j++;
593-
while (topic[j] != ' ' && j++ <= len)
593+
wordlen++;
594+
while (j < len && topic[j++] != ' ')
594595
wordlen++;
595596
}
596-
if (wordlen >= len) /* Don't try again if the same word */
597+
if (wordlen >= len)
597598
{
598-
if (!output)
599-
output = PageOutput(nl_count, pager ? &(pset.popt.topt) : NULL);
600-
break;
599+
/* Failed to shorten input, so try next pass if any */
600+
continue;
601601
}
602602
len = wordlen;
603603
}
604604

605-
/* Count newlines for pager */
605+
/*
606+
* Count newlines for pager. This logic must agree with what the
607+
* following loop will do!
608+
*/
609+
nl_count = 0;
606610
for (i = 0; QL_HELP[i].cmd; i++)
607611
{
608612
if (pg_strncasecmp(topic, QL_HELP[i].cmd, len) == 0 ||
609613
strcmp(topic, "*") == 0)
610614
{
611-
nl_count += 5 + QL_HELP[i].nl_count;
615+
/* magic constant here must match format below! */
616+
nl_count += 7 + QL_HELP[i].nl_count;
612617

613618
/* If we have an exact match, exit. Fixes \h SELECT */
614619
if (pg_strcasecmp(topic, QL_HELP[i].cmd) == 0)
615620
break;
616621
}
617622
}
623+
/* If no matches, don't open the output yet */
624+
if (nl_count == 0)
625+
continue;
618626

619627
if (!output)
620628
output = PageOutput(nl_count, pager ? &(pset.popt.topt) : NULL);
@@ -629,10 +637,10 @@ helpSQL(const char *topic, unsigned short int pager)
629637

630638
initPQExpBuffer(&buffer);
631639
QL_HELP[i].syntaxfunc(&buffer);
632-
help_found = true;
633640
url = psprintf("https://www.postgresql.org/docs/%s/%s.html",
634641
strstr(PG_VERSION, "devel") ? "devel" : PG_MAJORVERSION,
635642
QL_HELP[i].docbook_id);
643+
/* # of newlines in format must match constant above! */
636644
fprintf(output, _("Command: %s\n"
637645
"Description: %s\n"
638646
"Syntax:\n%s\n\n"
@@ -642,17 +650,24 @@ helpSQL(const char *topic, unsigned short int pager)
642650
buffer.data,
643651
url);
644652
free(url);
653+
termPQExpBuffer(&buffer);
654+
645655
/* If we have an exact match, exit. Fixes \h SELECT */
646656
if (pg_strcasecmp(topic, QL_HELP[i].cmd) == 0)
647657
break;
648658
}
649659
}
650-
if (help_found) /* Don't keep trying if we got a match */
651-
break;
660+
break;
652661
}
653662

654-
if (!help_found)
655-
fprintf(output, _("No help available for \"%s\".\nTry \\h with no arguments to see available help.\n"), topic);
663+
/* If we never found anything, report that */
664+
if (!output)
665+
{
666+
output = PageOutput(2, pager ? &(pset.popt.topt) : NULL);
667+
fprintf(output, _("No help available for \"%s\".\n"
668+
"Try \\h with no arguments to see available help.\n"),
669+
topic);
670+
}
656671

657672
ClosePager(output);
658673
}

0 commit comments

Comments
 (0)