1
0
Fork 0
mirror of https://github.com/git/git.git synced 2024-10-31 22:37:54 +01:00
git/sideband.c
Junio C Hamano 59a255aef0 sideband: do not read beyond the end of input
The caller of maybe_colorize_sideband() gives a counted buffer
<src, n>, but the callee checked src[] as if it were a NUL terminated
buffer.  If src[] had all isspace() bytes in it, we would have made
n negative, and then

 (1) made number of strncasecmp() calls to see if the remaining
     bytes in src[] matched keywords, reading beyond the end of the
     array (this actually happens even if n does not go negative),
     and/or

 (2) called strbuf_add() with negative count, most likely triggering
     the "you want to use way too much memory" error due to unsigned
     integer overflow.

Fix both issues by making sure we do not go beyond &src[n].

In the longer term we may want to accept size_t as parameter for
clarity (even though we know that a sideband message we are painting
typically would fit on a line on a terminal and int is sufficient).
Write it down as a NEEDSWORK comment.

Helped-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-18 09:16:48 -07:00

244 lines
5.8 KiB
C

#include "cache.h"
#include "color.h"
#include "config.h"
#include "pkt-line.h"
#include "sideband.h"
#include "help.h"
struct keyword_entry {
/*
* We use keyword as config key so it should be a single alphanumeric word.
*/
const char *keyword;
char color[COLOR_MAXLEN];
};
static struct keyword_entry keywords[] = {
{ "hint", GIT_COLOR_YELLOW },
{ "warning", GIT_COLOR_BOLD_YELLOW },
{ "success", GIT_COLOR_BOLD_GREEN },
{ "error", GIT_COLOR_BOLD_RED },
};
/* Returns a color setting (GIT_COLOR_NEVER, etc). */
static int use_sideband_colors(void)
{
static int use_sideband_colors_cached = -1;
const char *key = "color.remote";
struct strbuf sb = STRBUF_INIT;
char *value;
int i;
if (use_sideband_colors_cached >= 0)
return use_sideband_colors_cached;
if (!git_config_get_string(key, &value)) {
use_sideband_colors_cached = git_config_colorbool(key, value);
} else if (!git_config_get_string("color.ui", &value)) {
use_sideband_colors_cached = git_config_colorbool("color.ui", value);
} else {
use_sideband_colors_cached = GIT_COLOR_AUTO;
}
for (i = 0; i < ARRAY_SIZE(keywords); i++) {
strbuf_reset(&sb);
strbuf_addf(&sb, "%s.%s", key, keywords[i].keyword);
if (git_config_get_string(sb.buf, &value))
continue;
if (color_parse(value, keywords[i].color))
continue;
}
strbuf_release(&sb);
return use_sideband_colors_cached;
}
void list_config_color_sideband_slots(struct string_list *list, const char *prefix)
{
int i;
for (i = 0; i < ARRAY_SIZE(keywords); i++)
list_config_item(list, prefix, keywords[i].keyword);
}
/*
* Optionally highlight one keyword in remote output if it appears at the start
* of the line. This should be called for a single line only, which is
* passed as the first N characters of the SRC array.
*
* NEEDSWORK: use "size_t n" instead for clarity.
*/
static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n)
{
int i;
if (!want_color_stderr(use_sideband_colors())) {
strbuf_add(dest, src, n);
return;
}
while (0 < n && isspace(*src)) {
strbuf_addch(dest, *src);
src++;
n--;
}
for (i = 0; i < ARRAY_SIZE(keywords); i++) {
struct keyword_entry *p = keywords + i;
int len = strlen(p->keyword);
if (n <= len)
continue;
/*
* Match case insensitively, so we colorize output from existing
* servers regardless of the case that they use for their
* messages. We only highlight the word precisely, so
* "successful" stays uncolored.
*/
if (!strncasecmp(p->keyword, src, len) && !isalnum(src[len])) {
strbuf_addstr(dest, p->color);
strbuf_add(dest, src, len);
strbuf_addstr(dest, GIT_COLOR_RESET);
n -= len;
src += len;
break;
}
}
strbuf_add(dest, src, n);
}
/*
* Receive multiplexed output stream over git native protocol.
* in_stream is the input stream from the remote, which carries data
* in pkt_line format with band designator. Demultiplex it into out
* and err and return error appropriately. Band #1 carries the
* primary payload. Things coming over band #2 is not necessarily
* error; they are usually informative message on the standard error
* stream, aka "verbose"). A message over band #3 is a signal that
* the remote died unexpectedly. A flush() concludes the stream.
*/
#define DISPLAY_PREFIX "remote: "
#define ANSI_SUFFIX "\033[K"
#define DUMB_SUFFIX " "
int recv_sideband(const char *me, int in_stream, int out)
{
const char *suffix;
char buf[LARGE_PACKET_MAX + 1];
struct strbuf outbuf = STRBUF_INIT;
int retval = 0;
if (isatty(2) && !is_terminal_dumb())
suffix = ANSI_SUFFIX;
else
suffix = DUMB_SUFFIX;
while (!retval) {
const char *b, *brk;
int band, len;
len = packet_read(in_stream, NULL, NULL, buf, LARGE_PACKET_MAX, 0);
if (len == 0)
break;
if (len < 1) {
strbuf_addf(&outbuf,
"%s%s: protocol error: no band designator",
outbuf.len ? "\n" : "", me);
retval = SIDEBAND_PROTOCOL_ERROR;
break;
}
band = buf[0] & 0xff;
buf[len] = '\0';
len--;
switch (band) {
case 3:
strbuf_addf(&outbuf, "%s%s", outbuf.len ? "\n" : "",
DISPLAY_PREFIX);
maybe_colorize_sideband(&outbuf, buf + 1, len);
retval = SIDEBAND_REMOTE_ERROR;
break;
case 2:
b = buf + 1;
/*
* Append a suffix to each nonempty line to clear the
* end of the screen line.
*
* The output is accumulated in a buffer and
* each line is printed to stderr using
* write(2) to ensure inter-process atomicity.
*/
while ((brk = strpbrk(b, "\n\r"))) {
int linelen = brk - b;
if (!outbuf.len)
strbuf_addstr(&outbuf, DISPLAY_PREFIX);
if (linelen > 0) {
maybe_colorize_sideband(&outbuf, b, linelen);
strbuf_addstr(&outbuf, suffix);
}
strbuf_addch(&outbuf, *brk);
xwrite(2, outbuf.buf, outbuf.len);
strbuf_reset(&outbuf);
b = brk + 1;
}
if (*b) {
strbuf_addstr(&outbuf, outbuf.len ?
"" : DISPLAY_PREFIX);
maybe_colorize_sideband(&outbuf, b, strlen(b));
}
break;
case 1:
write_or_die(out, buf + 1, len);
break;
default:
strbuf_addf(&outbuf, "%s%s: protocol error: bad band #%d",
outbuf.len ? "\n" : "", me, band);
retval = SIDEBAND_PROTOCOL_ERROR;
break;
}
}
if (outbuf.len) {
strbuf_addch(&outbuf, '\n');
xwrite(2, outbuf.buf, outbuf.len);
}
strbuf_release(&outbuf);
return retval;
}
/*
* fd is connected to the remote side; send the sideband data
* over multiplexed packet stream.
*/
void send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max)
{
const char *p = data;
while (sz) {
unsigned n;
char hdr[5];
n = sz;
if (packet_max - 5 < n)
n = packet_max - 5;
if (0 <= band) {
xsnprintf(hdr, sizeof(hdr), "%04x", n + 5);
hdr[4] = band;
write_or_die(fd, hdr, 5);
} else {
xsnprintf(hdr, sizeof(hdr), "%04x", n + 4);
write_or_die(fd, hdr, 4);
}
write_or_die(fd, p, n);
p += n;
sz -= n;
}
}