NetBSD Problem Report #54281

From  Thu Jun  6 21:13:52 2019
Return-Path: <>
Received: from ( [])
	(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
	(Client CN "", Issuer " CA" (not verified))
	by (Postfix) with ESMTPS id 7C8E27A177
	for <>; Thu,  6 Jun 2019 21:13:52 +0000 (UTC)
Message-Id: <>
Date: Thu,  6 Jun 2019 21:13:51 +0000 (UTC)
Subject: libedit: rl_line_buffer content leaks cross-prompt
X-Send-Pr-Version: www-1.0

>Number:         54281
>Category:       lib
>Synopsis:       libedit: rl_line_buffer content leaks cross-prompt
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    lib-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Thu Jun 06 21:15:00 +0000 2019
>Last-Modified:  Fri Jun 07 15:25:00 +0000 2019
>Originator:     Jonathan Perkins
>Release:        Sources as of 2019/06/06
It appears that rl_line_buffer isn't getting null-terminated consistently when the length shortens.  This is particularly observable in tab completion, where users may look at rl_line_buffer to see the full user input.

I'm proposing to null-terminate rl_line_buffer on modification.  While rl_end allows the user to determine the end, the null terminator appears to be compatible with how readline is behaving.

The intent is that code like this should work:
const std::string full_line = collected_line_ + std::string(rl_line_buffer);
Example code is below.  To repro with a binary from it, type "example", press enter, then press tab to trigger completions.

With the current libedit, output should be:
Test> example
Test> example
This is because "example" has leaked cross-prompt and is visible to completion in rl_line_buffer.

With this fix, output should be:
Test> example
This is because rl_line_buffer gets null-terminated, making rl_line_buffer an empty string (correctly)

Example code:

#include <stdlib.h>
#include <string.h>
#include "readline.h"

static char** DoComplete(const char* text, int start, int end) {
  char** results = (char**)malloc(sizeof(*results) * 3);
  results[0] = strdup(rl_line_buffer);
  results[1] = strdup(rl_line_buffer);
  results[2] = nullptr;
  return results;

int main(int argc, char** argv) {
  for (;;) {
    char* result = readline("Test> ");
    if (result == nullptr) break;
    rl_attempted_completion_function = DoComplete;
  return 0;

--- old/libedit/readline.c
+++ new/libedit/readline.c
@@ -2231,6 +2231,7 @@ static void

 	rl_point = (int)(li->cursor - li->buffer);
 	rl_end = (int)(li->lastchar - li->buffer);
+	rl_line_buffer[rl_end] = '\0';


From: "Christos Zoulas" <>
Subject: PR/54281 CVS commit: src/lib/libedit
Date: Fri, 7 Jun 2019 11:21:48 -0400

 Module Name:	src
 Committed By:	christos
 Date:		Fri Jun  7 15:21:48 UTC 2019

 Modified Files:
 	src/lib/libedit: readline.c

 Log Message:
 PR/54281: Jonathan Perkins: NUL terminate rl_line_buffer on modification
 to avoid completion leak.

 To generate a diff of this commit:
 cvs rdiff -u -r1.154 -r1.155 src/lib/libedit/readline.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.43 2018/01/16 07:36:43 maya Exp $
$NetBSD:,v 1.9 2014/08/02 14:16:04 spz Exp $
Copyright © 1994-2017 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.