Unverified Commit 8e0b1729 by Patrick Steinhardt Committed by GitHub

Merge pull request #4834 from pks-t/pks/v0.27.5

Security release v0.27.5
parents 8b89f362 c590b41f
v0.27.5
-------
This is a security release fixing the following list of issues:
- Submodule URLs and paths with a leading "-" are now ignored.
This is due to the recently discovered CVE-2018-17456, which
can lead to arbitrary code execution in upstream git. While
libgit2 itself is not vulnerable, it can be used to inject
options in an implementation which performs a recursive clone
by executing an external command.
- When running repack while doing repo writes,
`packfile_load__cb()` could see some temporary files in the
directory that were bigger than the usual, and makes `memcmp`
overflow on the `p->pack_name` string. This issue was reported
and fixed by bisho.
- The configuration file parser used unbounded recursion to parse
multiline variables, which could lead to a stack overflow. The
issue was reported by the oss-fuzz project, issue 10048 and
fixed by Nelson Elhage.
- The fix to the unbounded recursion introduced a memory leak in
the config parser. While this leak was never in a public
release, the oss-fuzz project reported this as issue 10127. The
fix was implemented by Nelson Elhage and Patrick Steinhardt.
- When parsing "ok" packets received via the smart protocol, our
parsing code did not correctly verify the bounds of the
packets, which could result in a heap-buffer overflow. The
issue was reported by the oss-fuzz project, issue 9749 and
fixed by Patrick Steinhardt.
- The parsing code for the smart protocol has been tightened in
general, fixing heap-buffer overflows when parsing the packet
type as well as for "ACK" and "unpack" packets. The issue was
discovered and fixed by Patrick Steinhardt.
- Fixed potential integer overflows on platforms with 16 bit
integers when parsing packets for the smart protocol. The issue
was discovered and fixed by Patrick Steinhardt.
- Fixed potential NULL pointer dereference when parsing
configuration files which have "include.path" or
"includeIf..path" statements without a value.
v0.27.4 v0.27.4
------- -------
......
...@@ -7,10 +7,10 @@ ...@@ -7,10 +7,10 @@
#ifndef INCLUDE_git_version_h__ #ifndef INCLUDE_git_version_h__
#define INCLUDE_git_version_h__ #define INCLUDE_git_version_h__
#define LIBGIT2_VERSION "0.27.4" #define LIBGIT2_VERSION "0.27.5"
#define LIBGIT2_VER_MAJOR 0 #define LIBGIT2_VER_MAJOR 0
#define LIBGIT2_VER_MINOR 27 #define LIBGIT2_VER_MINOR 27
#define LIBGIT2_VER_REVISION 4 #define LIBGIT2_VER_REVISION 5
#define LIBGIT2_VER_PATCH 0 #define LIBGIT2_VER_PATCH 0
#define LIBGIT2_SOVERSION 27 #define LIBGIT2_SOVERSION 27
......
...@@ -928,6 +928,9 @@ static int parse_include(git_config_parser *reader, ...@@ -928,6 +928,9 @@ static int parse_include(git_config_parser *reader,
char *dir; char *dir;
int result; int result;
if (!file)
return 0;
if ((result = git_path_dirname_r(&path, reader->file->path)) < 0) if ((result = git_path_dirname_r(&path, reader->file->path)) < 0)
return result; return result;
...@@ -1029,7 +1032,7 @@ static int parse_conditional_include(git_config_parser *reader, ...@@ -1029,7 +1032,7 @@ static int parse_conditional_include(git_config_parser *reader,
size_t i; size_t i;
int error = 0, matches; int error = 0, matches;
if (!parse_data->repo) if (!parse_data->repo || !file)
return 0; return 0;
condition = git__substrdup(section + strlen("includeIf."), condition = git__substrdup(section + strlen("includeIf."),
......
...@@ -315,49 +315,51 @@ done: ...@@ -315,49 +315,51 @@ done:
static int parse_multiline_variable(git_config_parser *reader, git_buf *value, int in_quotes) static int parse_multiline_variable(git_config_parser *reader, git_buf *value, int in_quotes)
{ {
char *line = NULL, *proc_line = NULL;
int quote_count; int quote_count;
bool multiline; bool multiline = true;
while (multiline) {
char *line = NULL, *proc_line = NULL;
int error;
/* Check that the next line exists */ /* Check that the next line exists */
git_parse_advance_line(&reader->ctx); git_parse_advance_line(&reader->ctx);
line = git__strndup(reader->ctx.line, reader->ctx.line_len); line = git__strndup(reader->ctx.line, reader->ctx.line_len);
if (line == NULL) GITERR_CHECK_ALLOC(line);
return -1;
/* We've reached the end of the file, there is no continuation. /*
* We've reached the end of the file, there is no continuation.
* (this is not an error). * (this is not an error).
*/ */
if (line[0] == '\0') { if (line[0] == '\0') {
git__free(line); error = 0;
return 0; goto out;
} }
/* If it was just a comment, pretend it didn't exist */
quote_count = strip_comments(line, !!in_quotes); quote_count = strip_comments(line, !!in_quotes);
if (line[0] == '\0')
goto next;
/* If it was just a comment, pretend it didn't exist */ if ((error = unescape_line(&proc_line, &multiline,
if (line[0] == '\0') { line, in_quotes)) < 0)
git__free(line); goto out;
return parse_multiline_variable(reader, value, quote_count);
/* TODO: unbounded recursion. This **could** be exploitable */
}
if (unescape_line(&proc_line, &multiline, line, in_quotes) < 0) { /* Add this line to the multiline var */
git__free(line); if ((error = git_buf_puts(value, proc_line)) < 0)
return -1; goto out;
}
/* add this line to the multiline var */
git_buf_puts(value, proc_line); next:
git__free(line); git__free(line);
git__free(proc_line); git__free(proc_line);
in_quotes = quote_count;
continue;
/* out:
* If we need to continue reading the next line, let's just git__free(line);
* keep putting stuff in the buffer git__free(proc_line);
*/ return error;
if (multiline) }
return parse_multiline_variable(reader, value, quote_count);
return 0; return 0;
} }
......
...@@ -210,7 +210,7 @@ static int packfile_load__cb(void *data, git_buf *path) ...@@ -210,7 +210,7 @@ static int packfile_load__cb(void *data, git_buf *path)
for (i = 0; i < backend->packs.length; ++i) { for (i = 0; i < backend->packs.length; ++i) {
struct git_pack_file *p = git_vector_get(&backend->packs, i); struct git_pack_file *p = git_vector_get(&backend->packs, i);
if (memcmp(p->pack_name, path_str, cmp_len) == 0) if (strncmp(p->pack_name, path_str, cmp_len) == 0)
return 0; return 0;
} }
......
...@@ -1813,6 +1813,14 @@ static int get_value(const char **out, git_config *cfg, git_buf *buf, const char ...@@ -1813,6 +1813,14 @@ static int get_value(const char **out, git_config *cfg, git_buf *buf, const char
return error; return error;
} }
static bool looks_like_command_line_option(const char *s)
{
if (s && s[0] == '-')
return true;
return false;
}
static int submodule_read_config(git_submodule *sm, git_config *cfg) static int submodule_read_config(git_submodule *sm, git_config *cfg)
{ {
git_buf key = GIT_BUF_INIT; git_buf key = GIT_BUF_INIT;
...@@ -1826,6 +1834,8 @@ static int submodule_read_config(git_submodule *sm, git_config *cfg) ...@@ -1826,6 +1834,8 @@ static int submodule_read_config(git_submodule *sm, git_config *cfg)
if ((error = get_value(&value, cfg, &key, sm->name, "path")) == 0) { if ((error = get_value(&value, cfg, &key, sm->name, "path")) == 0) {
in_config = 1; in_config = 1;
/* We would warn here if we had that API */
if (!looks_like_command_line_option(value)) {
/* /*
* TODO: if case insensitive filesystem, then the following strcmp * TODO: if case insensitive filesystem, then the following strcmp
* should be strcasecmp * should be strcasecmp
...@@ -1836,14 +1846,19 @@ static int submodule_read_config(git_submodule *sm, git_config *cfg) ...@@ -1836,14 +1846,19 @@ static int submodule_read_config(git_submodule *sm, git_config *cfg)
sm->path = git__strdup(value); sm->path = git__strdup(value);
GITERR_CHECK_ALLOC(sm->path); GITERR_CHECK_ALLOC(sm->path);
} }
}
} else if (error != GIT_ENOTFOUND) { } else if (error != GIT_ENOTFOUND) {
goto cleanup; goto cleanup;
} }
if ((error = get_value(&value, cfg, &key, sm->name, "url")) == 0) { if ((error = get_value(&value, cfg, &key, sm->name, "url")) == 0) {
/* We would warn here if we had that API */
if (!looks_like_command_line_option(value)) {
in_config = 1; in_config = 1;
sm->url = git__strdup(value); sm->url = git__strdup(value);
GITERR_CHECK_ALLOC(sm->url); GITERR_CHECK_ALLOC(sm->url);
}
} else if (error != GIT_ENOTFOUND) { } else if (error != GIT_ENOTFOUND) {
goto cleanup; goto cleanup;
} }
......
...@@ -33,14 +33,14 @@ ...@@ -33,14 +33,14 @@
extern bool git_smart__ofs_delta_enabled; extern bool git_smart__ofs_delta_enabled;
enum git_pkt_type { typedef enum {
GIT_PKT_CMD, GIT_PKT_CMD,
GIT_PKT_FLUSH, GIT_PKT_FLUSH,
GIT_PKT_REF, GIT_PKT_REF,
GIT_PKT_HAVE, GIT_PKT_HAVE,
GIT_PKT_ACK, GIT_PKT_ACK,
GIT_PKT_NAK, GIT_PKT_NAK,
GIT_PKT_PACK, GIT_PKT_PACK__UNUSED,
GIT_PKT_COMMENT, GIT_PKT_COMMENT,
GIT_PKT_ERR, GIT_PKT_ERR,
GIT_PKT_DATA, GIT_PKT_DATA,
...@@ -48,7 +48,7 @@ enum git_pkt_type { ...@@ -48,7 +48,7 @@ enum git_pkt_type {
GIT_PKT_OK, GIT_PKT_OK,
GIT_PKT_NG, GIT_PKT_NG,
GIT_PKT_UNPACK, GIT_PKT_UNPACK,
}; } git_pkt_type;
/* Used for multi_ack and mutli_ack_detailed */ /* Used for multi_ack and mutli_ack_detailed */
enum git_ack_status { enum git_ack_status {
...@@ -60,11 +60,11 @@ enum git_ack_status { ...@@ -60,11 +60,11 @@ enum git_ack_status {
/* This would be a flush pkt */ /* This would be a flush pkt */
typedef struct { typedef struct {
enum git_pkt_type type; git_pkt_type type;
} git_pkt; } git_pkt;
struct git_pkt_cmd { struct git_pkt_cmd {
enum git_pkt_type type; git_pkt_type type;
char *cmd; char *cmd;
char *path; char *path;
char *host; char *host;
...@@ -72,50 +72,50 @@ struct git_pkt_cmd { ...@@ -72,50 +72,50 @@ struct git_pkt_cmd {
/* This is a pkt-line with some info in it */ /* This is a pkt-line with some info in it */
typedef struct { typedef struct {
enum git_pkt_type type; git_pkt_type type;
git_remote_head head; git_remote_head head;
char *capabilities; char *capabilities;
} git_pkt_ref; } git_pkt_ref;
/* Useful later */ /* Useful later */
typedef struct { typedef struct {
enum git_pkt_type type; git_pkt_type type;
git_oid oid; git_oid oid;
enum git_ack_status status; enum git_ack_status status;
} git_pkt_ack; } git_pkt_ack;
typedef struct { typedef struct {
enum git_pkt_type type; git_pkt_type type;
char comment[GIT_FLEX_ARRAY]; char comment[GIT_FLEX_ARRAY];
} git_pkt_comment; } git_pkt_comment;
typedef struct { typedef struct {
enum git_pkt_type type; git_pkt_type type;
int len; size_t len;
char data[GIT_FLEX_ARRAY]; char data[GIT_FLEX_ARRAY];
} git_pkt_data; } git_pkt_data;
typedef git_pkt_data git_pkt_progress; typedef git_pkt_data git_pkt_progress;
typedef struct { typedef struct {
enum git_pkt_type type; git_pkt_type type;
int len; size_t len;
char error[GIT_FLEX_ARRAY]; char error[GIT_FLEX_ARRAY];
} git_pkt_err; } git_pkt_err;
typedef struct { typedef struct {
enum git_pkt_type type; git_pkt_type type;
char *ref; char *ref;
} git_pkt_ok; } git_pkt_ok;
typedef struct { typedef struct {
enum git_pkt_type type; git_pkt_type type;
char *ref; char *ref;
char *msg; char *msg;
} git_pkt_ng; } git_pkt_ng;
typedef struct { typedef struct {
enum git_pkt_type type; git_pkt_type type;
int unpack_ok; int unpack_ok;
} git_pkt_unpack; } git_pkt_unpack;
...@@ -189,7 +189,7 @@ int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream ...@@ -189,7 +189,7 @@ int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream
int git_smart__update_heads(transport_smart *t, git_vector *symrefs); int git_smart__update_heads(transport_smart *t, git_vector *symrefs);
/* smart_pkt.c */ /* smart_pkt.c */
int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_t len); int git_pkt_parse_line(git_pkt **head, const char **endptr, const char *line, size_t linelen);
int git_pkt_buffer_flush(git_buf *buf); int git_pkt_buffer_flush(git_buf *buf);
int git_pkt_send_flush(GIT_SOCKET s); int git_pkt_send_flush(GIT_SOCKET s);
int git_pkt_buffer_done(git_buf *buf); int git_pkt_buffer_done(git_buf *buf);
......
...@@ -43,34 +43,43 @@ static int flush_pkt(git_pkt **out) ...@@ -43,34 +43,43 @@ static int flush_pkt(git_pkt **out)
static int ack_pkt(git_pkt **out, const char *line, size_t len) static int ack_pkt(git_pkt **out, const char *line, size_t len)
{ {
git_pkt_ack *pkt; git_pkt_ack *pkt;
GIT_UNUSED(line);
GIT_UNUSED(len);
pkt = git__calloc(1, sizeof(git_pkt_ack)); pkt = git__calloc(1, sizeof(git_pkt_ack));
GITERR_CHECK_ALLOC(pkt); GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_ACK; pkt->type = GIT_PKT_ACK;
line += 3;
len -= 3;
if (len >= GIT_OID_HEXSZ) { if (git__prefixncmp(line, len, "ACK "))
git_oid_fromstr(&pkt->oid, line + 1); goto out_err;
line += GIT_OID_HEXSZ + 1; line += 4;
len -= GIT_OID_HEXSZ + 1; len -= 4;
}
if (len < GIT_OID_HEXSZ || git_oid_fromstr(&pkt->oid, line) < 0)
goto out_err;
line += GIT_OID_HEXSZ;
len -= GIT_OID_HEXSZ;
if (len && line[0] == ' ') {
line++;
len--;
if (len >= 7) { if (!git__prefixncmp(line, len, "continue"))
if (!git__prefixcmp(line + 1, "continue"))
pkt->status = GIT_ACK_CONTINUE; pkt->status = GIT_ACK_CONTINUE;
if (!git__prefixcmp(line + 1, "common")) else if (!git__prefixncmp(line, len, "common"))
pkt->status = GIT_ACK_COMMON; pkt->status = GIT_ACK_COMMON;
if (!git__prefixcmp(line + 1, "ready")) else if (!git__prefixncmp(line, len, "ready"))
pkt->status = GIT_ACK_READY; pkt->status = GIT_ACK_READY;
else
goto out_err;
} }
*out = (git_pkt *) pkt; *out = (git_pkt *) pkt;
return 0; return 0;
out_err:
giterr_set(GITERR_NET, "error parsing ACK pkt-line");
git__free(pkt);
return -1;
} }
static int nak_pkt(git_pkt **out) static int nak_pkt(git_pkt **out)
...@@ -86,19 +95,6 @@ static int nak_pkt(git_pkt **out) ...@@ -86,19 +95,6 @@ static int nak_pkt(git_pkt **out)
return 0; return 0;
} }
static int pack_pkt(git_pkt **out)
{
git_pkt *pkt;
pkt = git__malloc(sizeof(git_pkt));
GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_PACK;
*out = pkt;
return 0;
}
static int comment_pkt(git_pkt **out, const char *line, size_t len) static int comment_pkt(git_pkt **out, const char *line, size_t len)
{ {
git_pkt_comment *pkt; git_pkt_comment *pkt;
...@@ -120,10 +116,12 @@ static int comment_pkt(git_pkt **out, const char *line, size_t len) ...@@ -120,10 +116,12 @@ static int comment_pkt(git_pkt **out, const char *line, size_t len)
static int err_pkt(git_pkt **out, const char *line, size_t len) static int err_pkt(git_pkt **out, const char *line, size_t len)
{ {
git_pkt_err *pkt; git_pkt_err *pkt = NULL;
size_t alloclen; size_t alloclen;
/* Remove "ERR " from the line */ /* Remove "ERR " from the line */
if (git__prefixncmp(line, len, "ERR "))
goto out_err;
line += 4; line += 4;
len -= 4; len -= 4;
...@@ -131,15 +129,20 @@ static int err_pkt(git_pkt **out, const char *line, size_t len) ...@@ -131,15 +129,20 @@ static int err_pkt(git_pkt **out, const char *line, size_t len)
GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1); GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
pkt = git__malloc(alloclen); pkt = git__malloc(alloclen);
GITERR_CHECK_ALLOC(pkt); GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_ERR; pkt->type = GIT_PKT_ERR;
pkt->len = (int)len; pkt->len = len;
memcpy(pkt->error, line, len); memcpy(pkt->error, line, len);
pkt->error[len] = '\0'; pkt->error[len] = '\0';
*out = (git_pkt *) pkt; *out = (git_pkt *) pkt;
return 0; return 0;
out_err:
giterr_set(GITERR_NET, "error parsing ERR pkt-line");
git__free(pkt);
return -1;
} }
static int data_pkt(git_pkt **out, const char *line, size_t len) static int data_pkt(git_pkt **out, const char *line, size_t len)
...@@ -155,7 +158,7 @@ static int data_pkt(git_pkt **out, const char *line, size_t len) ...@@ -155,7 +158,7 @@ static int data_pkt(git_pkt **out, const char *line, size_t len)
GITERR_CHECK_ALLOC(pkt); GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_DATA; pkt->type = GIT_PKT_DATA;
pkt->len = (int) len; pkt->len = len;
memcpy(pkt->data, line, len); memcpy(pkt->data, line, len);
*out = (git_pkt *) pkt; *out = (git_pkt *) pkt;
...@@ -176,7 +179,7 @@ static int sideband_progress_pkt(git_pkt **out, const char *line, size_t len) ...@@ -176,7 +179,7 @@ static int sideband_progress_pkt(git_pkt **out, const char *line, size_t len)
GITERR_CHECK_ALLOC(pkt); GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_PROGRESS; pkt->type = GIT_PKT_PROGRESS;
pkt->len = (int) len; pkt->len = len;
memcpy(pkt->data, line, len); memcpy(pkt->data, line, len);
*out = (git_pkt *) pkt; *out = (git_pkt *) pkt;
...@@ -212,28 +215,25 @@ static int sideband_error_pkt(git_pkt **out, const char *line, size_t len) ...@@ -212,28 +215,25 @@ static int sideband_error_pkt(git_pkt **out, const char *line, size_t len)
*/ */
static int ref_pkt(git_pkt **out, const char *line, size_t len) static int ref_pkt(git_pkt **out, const char *line, size_t len)
{ {
int error;
git_pkt_ref *pkt; git_pkt_ref *pkt;
size_t alloclen; size_t alloclen;
pkt = git__malloc(sizeof(git_pkt_ref)); pkt = git__calloc(1, sizeof(git_pkt_ref));
GITERR_CHECK_ALLOC(pkt); GITERR_CHECK_ALLOC(pkt);
memset(pkt, 0x0, sizeof(git_pkt_ref));
pkt->type = GIT_PKT_REF; pkt->type = GIT_PKT_REF;
if ((error = git_oid_fromstr(&pkt->head.oid, line)) < 0)
goto error_out;
/* Check for a bit of consistency */
if (line[GIT_OID_HEXSZ] != ' ') {
giterr_set(GITERR_NET, "error parsing pkt-line");
error = -1;
goto error_out;
}
/* Jump from the name */ if (len < GIT_OID_HEXSZ || git_oid_fromstr(&pkt->head.oid, line) < 0)
line += GIT_OID_HEXSZ + 1; goto out_err;
len -= (GIT_OID_HEXSZ + 1); line += GIT_OID_HEXSZ;
len -= GIT_OID_HEXSZ;
if (git__prefixncmp(line, len, " "))
goto out_err;
line++;
len--;
if (!len)
goto out_err;
if (line[len - 1] == '\n') if (line[len - 1] == '\n')
--len; --len;
...@@ -245,36 +245,36 @@ static int ref_pkt(git_pkt **out, const char *line, size_t len) ...@@ -245,36 +245,36 @@ static int ref_pkt(git_pkt **out, const char *line, size_t len)
memcpy(pkt->head.name, line, len); memcpy(pkt->head.name, line, len);
pkt->head.name[len] = '\0'; pkt->head.name[len] = '\0';
if (strlen(pkt->head.name) < len) { if (strlen(pkt->head.name) < len)
pkt->capabilities = strchr(pkt->head.name, '\0') + 1; pkt->capabilities = strchr(pkt->head.name, '\0') + 1;
}
*out = (git_pkt *)pkt; *out = (git_pkt *)pkt;
return 0; return 0;
error_out: out_err:
giterr_set(GITERR_NET, "error parsing REF pkt-line");
if (pkt)
git__free(pkt->head.name);
git__free(pkt); git__free(pkt);
return error; return -1;
} }
static int ok_pkt(git_pkt **out, const char *line, size_t len) static int ok_pkt(git_pkt **out, const char *line, size_t len)
{ {
git_pkt_ok *pkt; git_pkt_ok *pkt;
const char *ptr;
size_t alloc_len; size_t alloc_len;
pkt = git__malloc(sizeof(*pkt)); pkt = git__malloc(sizeof(*pkt));
GITERR_CHECK_ALLOC(pkt); GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_OK; pkt->type = GIT_PKT_OK;
line += 3; /* skip "ok " */ if (git__prefixncmp(line, len, "ok "))
if (!(ptr = strchr(line, '\n'))) { goto out_err;
giterr_set(GITERR_NET, "invalid packet line"); line += 3;
git__free(pkt); len -= 3;
return -1;
} if (line[len - 1] == '\n')
len = ptr - line; --len;
GITERR_CHECK_ALLOC_ADD(&alloc_len, len, 1); GITERR_CHECK_ALLOC_ADD(&alloc_len, len, 1);
pkt->ref = git__malloc(alloc_len); pkt->ref = git__malloc(alloc_len);
...@@ -285,12 +285,17 @@ static int ok_pkt(git_pkt **out, const char *line, size_t len) ...@@ -285,12 +285,17 @@ static int ok_pkt(git_pkt **out, const char *line, size_t len)
*out = (git_pkt *)pkt; *out = (git_pkt *)pkt;
return 0; return 0;
out_err:
giterr_set(GITERR_NET, "error parsing OK pkt-line");
git__free(pkt);
return -1;
} }
static int ng_pkt(git_pkt **out, const char *line, size_t len) static int ng_pkt(git_pkt **out, const char *line, size_t len)
{ {
git_pkt_ng *pkt; git_pkt_ng *pkt;
const char *ptr; const char *ptr, *eol;
size_t alloclen; size_t alloclen;
pkt = git__malloc(sizeof(*pkt)); pkt = git__malloc(sizeof(*pkt));
...@@ -299,11 +304,13 @@ static int ng_pkt(git_pkt **out, const char *line, size_t len) ...@@ -299,11 +304,13 @@ static int ng_pkt(git_pkt **out, const char *line, size_t len)
pkt->ref = NULL; pkt->ref = NULL;
pkt->type = GIT_PKT_NG; pkt->type = GIT_PKT_NG;
if (len < 3) eol = line + len;
if (git__prefixncmp(line, len, "ng "))
goto out_err; goto out_err;
line += 3; /* skip "ng " */ line += 3;
len -= 3;
if (!(ptr = memchr(line, ' ', len))) if (!(ptr = memchr(line, ' ', eol - line)))
goto out_err; goto out_err;
len = ptr - line; len = ptr - line;
...@@ -314,11 +321,11 @@ static int ng_pkt(git_pkt **out, const char *line, size_t len) ...@@ -314,11 +321,11 @@ static int ng_pkt(git_pkt **out, const char *line, size_t len)
memcpy(pkt->ref, line, len); memcpy(pkt->ref, line, len);
pkt->ref[len] = '\0'; pkt->ref[len] = '\0';
if (len < 1)
goto out_err;
line = ptr + 1; line = ptr + 1;
len -= 1; if (line >= eol)
if (!(ptr = memchr(line, '\n', len))) goto out_err;
if (!(ptr = memchr(line, '\n', eol - line)))
goto out_err; goto out_err;
len = ptr - line; len = ptr - line;
...@@ -343,13 +350,11 @@ static int unpack_pkt(git_pkt **out, const char *line, size_t len) ...@@ -343,13 +350,11 @@ static int unpack_pkt(git_pkt **out, const char *line, size_t len)
{ {
git_pkt_unpack *pkt; git_pkt_unpack *pkt;
GIT_UNUSED(len);
pkt = git__malloc(sizeof(*pkt)); pkt = git__malloc(sizeof(*pkt));
GITERR_CHECK_ALLOC(pkt); GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_UNPACK; pkt->type = GIT_PKT_UNPACK;
if (!git__prefixcmp(line, "unpack ok"))
if (!git__prefixncmp(line, len, "unpack ok"))
pkt->unpack_ok = 1; pkt->unpack_ok = 1;
else else
pkt->unpack_ok = 0; pkt->unpack_ok = 0;
...@@ -358,13 +363,17 @@ static int unpack_pkt(git_pkt **out, const char *line, size_t len) ...@@ -358,13 +363,17 @@ static int unpack_pkt(git_pkt **out, const char *line, size_t len)
return 0; return 0;
} }
static int32_t parse_len(const char *line) static int parse_len(size_t *out, const char *line, size_t linelen)
{ {
char num[PKT_LEN_SIZE + 1]; char num[PKT_LEN_SIZE + 1];
int i, k, error; int i, k, error;
int32_t len; int32_t len;
const char *num_end; const char *num_end;
/* Not even enough for the length */
if (linelen < PKT_LEN_SIZE)
return GIT_EBUFS;
memcpy(num, line, PKT_LEN_SIZE); memcpy(num, line, PKT_LEN_SIZE);
num[PKT_LEN_SIZE] = '\0'; num[PKT_LEN_SIZE] = '\0';
...@@ -385,7 +394,11 @@ static int32_t parse_len(const char *line) ...@@ -385,7 +394,11 @@ static int32_t parse_len(const char *line)
if ((error = git__strtol32(&len, num, &num_end, 16)) < 0) if ((error = git__strtol32(&len, num, &num_end, 16)) < 0)
return error; return error;
return len; if (len < 0)
return -1;
*out = (size_t) len;
return 0;
} }
/* /*
...@@ -402,35 +415,32 @@ static int32_t parse_len(const char *line) ...@@ -402,35 +415,32 @@ static int32_t parse_len(const char *line)
*/ */
int git_pkt_parse_line( int git_pkt_parse_line(
git_pkt **head, const char *line, const char **out, size_t bufflen) git_pkt **pkt, const char **endptr, const char *line, size_t linelen)
{ {
int ret; int error;
int32_t len; size_t len;
/* Not even enough for the length */
if (bufflen > 0 && bufflen < PKT_LEN_SIZE)
return GIT_EBUFS;
len = parse_len(line); if ((error = parse_len(&len, line, linelen)) < 0) {
if (len < 0) {
/* /*
* If we fail to parse the length, it might be because the * If we fail to parse the length, it might be
* server is trying to send us the packfile already. * because the server is trying to send us the
* packfile already or because we do not yet have
* enough data.
*/ */
if (bufflen >= 4 && !git__prefixcmp(line, "PACK")) { if (error == GIT_EBUFS)
giterr_clear(); ;
*out = line; else if (!git__prefixncmp(line, linelen, "PACK"))
return pack_pkt(head); giterr_set(GITERR_NET, "unexpected pack file");
} else
giterr_set(GITERR_NET, "bad packet length");
return (int)len; return error;
} }
/* /*
* If we were given a buffer length, then make sure there is * Make sure there is enough in the buffer to satisfy
* enough in the buffer to satisfy this line * this line.
*/ */
if (bufflen > 0 && bufflen < (size_t)len) if (linelen < len)
return GIT_EBUFS; return GIT_EBUFS;
/* /*
...@@ -453,38 +463,38 @@ int git_pkt_parse_line( ...@@ -453,38 +463,38 @@ int git_pkt_parse_line(
} }
if (len == 0) { /* Flush pkt */ if (len == 0) { /* Flush pkt */
*out = line; *endptr = line;
return flush_pkt(head); return flush_pkt(pkt);
} }
len -= PKT_LEN_SIZE; /* the encoded length includes its own size */ len -= PKT_LEN_SIZE; /* the encoded length includes its own size */
if (*line == GIT_SIDE_BAND_DATA) if (*line == GIT_SIDE_BAND_DATA)
ret = data_pkt(head, line, len); error = data_pkt(pkt, line, len);
else if (*line == GIT_SIDE_BAND_PROGRESS) else if (*line == GIT_SIDE_BAND_PROGRESS)
ret = sideband_progress_pkt(head, line, len); error = sideband_progress_pkt(pkt, line, len);
else if (*line == GIT_SIDE_BAND_ERROR) else if (*line == GIT_SIDE_BAND_ERROR)
ret = sideband_error_pkt(head, line, len); error = sideband_error_pkt(pkt, line, len);
else if (!git__prefixcmp(line, "ACK")) else if (!git__prefixncmp(line, len, "ACK"))
ret = ack_pkt(head, line, len); error = ack_pkt(pkt, line, len);
else if (!git__prefixcmp(line, "NAK")) else if (!git__prefixncmp(line, len, "NAK"))
ret = nak_pkt(head); error = nak_pkt(pkt);
else if (!git__prefixcmp(line, "ERR ")) else if (!git__prefixncmp(line, len, "ERR"))
ret = err_pkt(head, line, len); error = err_pkt(pkt, line, len);
else if (*line == '#') else if (*line == '#')
ret = comment_pkt(head, line, len); error = comment_pkt(pkt, line, len);
else if (!git__prefixcmp(line, "ok")) else if (!git__prefixncmp(line, len, "ok"))
ret = ok_pkt(head, line, len); error = ok_pkt(pkt, line, len);
else if (!git__prefixcmp(line, "ng")) else if (!git__prefixncmp(line, len, "ng"))
ret = ng_pkt(head, line, len); error = ng_pkt(pkt, line, len);
else if (!git__prefixcmp(line, "unpack")) else if (!git__prefixncmp(line, len, "unpack"))
ret = unpack_pkt(head, line, len); error = unpack_pkt(pkt, line, len);
else else
ret = ref_pkt(head, line, len); error = ref_pkt(pkt, line, len);
*out = line + len; *endptr = line + len;
return ret; return error;
} }
void git_pkt_free(git_pkt *pkt) void git_pkt_free(git_pkt *pkt)
......
...@@ -44,7 +44,7 @@ int git_smart__store_refs(transport_smart *t, int flushes) ...@@ -44,7 +44,7 @@ int git_smart__store_refs(transport_smart *t, int flushes)
do { do {
if (buf->offset > 0) if (buf->offset > 0)
error = git_pkt_parse_line(&pkt, buf->data, &line_end, buf->offset); error = git_pkt_parse_line(&pkt, &line_end, buf->data, buf->offset);
else else
error = GIT_EBUFS; error = GIT_EBUFS;
...@@ -209,15 +209,15 @@ int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vec ...@@ -209,15 +209,15 @@ int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vec
return 0; return 0;
} }
static int recv_pkt(git_pkt **out, gitno_buffer *buf) static int recv_pkt(git_pkt **out, git_pkt_type *pkt_type, gitno_buffer *buf)
{ {
const char *ptr = buf->data, *line_end = ptr; const char *ptr = buf->data, *line_end = ptr;
git_pkt *pkt = NULL; git_pkt *pkt = NULL;
int pkt_type, error = 0, ret; int error = 0, ret;
do { do {
if (buf->offset > 0) if (buf->offset > 0)
error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->offset); error = git_pkt_parse_line(&pkt, &line_end, ptr, buf->offset);
else else
error = GIT_EBUFS; error = GIT_EBUFS;
...@@ -236,13 +236,14 @@ static int recv_pkt(git_pkt **out, gitno_buffer *buf) ...@@ -236,13 +236,14 @@ static int recv_pkt(git_pkt **out, gitno_buffer *buf)
} while (error); } while (error);
gitno_consume(buf, line_end); gitno_consume(buf, line_end);
pkt_type = pkt->type; if (pkt_type)
*pkt_type = pkt->type;
if (out != NULL) if (out != NULL)
*out = pkt; *out = pkt;
else else
git__free(pkt); git__free(pkt);
return pkt_type; return error;
} }
static int store_common(transport_smart *t) static int store_common(transport_smart *t)
...@@ -252,7 +253,7 @@ static int store_common(transport_smart *t) ...@@ -252,7 +253,7 @@ static int store_common(transport_smart *t)
int error; int error;
do { do {
if ((error = recv_pkt(&pkt, buf)) < 0) if ((error = recv_pkt(&pkt, NULL, buf)) < 0)
return error; return error;
if (pkt->type == GIT_PKT_ACK) { if (pkt->type == GIT_PKT_ACK) {
...@@ -320,7 +321,7 @@ static int wait_while_ack(gitno_buffer *buf) ...@@ -320,7 +321,7 @@ static int wait_while_ack(gitno_buffer *buf)
while (1) { while (1) {
git__free(pkt); git__free(pkt);
if ((error = recv_pkt((git_pkt **)&pkt, buf)) < 0) if ((error = recv_pkt((git_pkt **)&pkt, NULL, buf)) < 0)
return error; return error;
if (pkt->type == GIT_PKT_NAK) if (pkt->type == GIT_PKT_NAK)
...@@ -345,7 +346,8 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c ...@@ -345,7 +346,8 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
gitno_buffer *buf = &t->buffer; gitno_buffer *buf = &t->buffer;
git_buf data = GIT_BUF_INIT; git_buf data = GIT_BUF_INIT;
git_revwalk *walk = NULL; git_revwalk *walk = NULL;
int error = -1, pkt_type; int error = -1;
git_pkt_type pkt_type;
unsigned int i; unsigned int i;
git_oid oid; git_oid oid;
...@@ -395,16 +397,13 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c ...@@ -395,16 +397,13 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
if ((error = store_common(t)) < 0) if ((error = store_common(t)) < 0)
goto on_error; goto on_error;
} else { } else {
pkt_type = recv_pkt(NULL, buf); error = recv_pkt(NULL, &pkt_type, buf);
if (error < 0) {
if (pkt_type == GIT_PKT_ACK) { goto on_error;
} else if (pkt_type == GIT_PKT_ACK) {
break; break;
} else if (pkt_type == GIT_PKT_NAK) { } else if (pkt_type == GIT_PKT_NAK) {
continue; continue;
} else if (pkt_type < 0) {
/* recv_pkt returned an error */
error = pkt_type;
goto on_error;
} else { } else {
giterr_set(GITERR_NET, "Unexpected pkt type"); giterr_set(GITERR_NET, "Unexpected pkt type");
error = -1; error = -1;
...@@ -470,10 +469,10 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c ...@@ -470,10 +469,10 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
/* Now let's eat up whatever the server gives us */ /* Now let's eat up whatever the server gives us */
if (!t->caps.multi_ack && !t->caps.multi_ack_detailed) { if (!t->caps.multi_ack && !t->caps.multi_ack_detailed) {
pkt_type = recv_pkt(NULL, buf); error = recv_pkt(NULL, &pkt_type, buf);
if (pkt_type < 0) { if (error < 0) {
return pkt_type; return error;
} else if (pkt_type != GIT_PKT_ACK && pkt_type != GIT_PKT_NAK) { } else if (pkt_type != GIT_PKT_ACK && pkt_type != GIT_PKT_NAK) {
giterr_set(GITERR_NET, "Unexpected pkt type"); giterr_set(GITERR_NET, "Unexpected pkt type");
return -1; return -1;
...@@ -594,7 +593,7 @@ int git_smart__download_pack( ...@@ -594,7 +593,7 @@ int git_smart__download_pack(
goto done; goto done;
} }
if ((error = recv_pkt(&pkt, buf)) >= 0) { if ((error = recv_pkt(&pkt, NULL, buf)) >= 0) {
/* Check cancellation after network call */ /* Check cancellation after network call */
if (t->cancelled.val) { if (t->cancelled.val) {
giterr_clear(); giterr_clear();
...@@ -752,7 +751,7 @@ static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt, ...@@ -752,7 +751,7 @@ static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt,
} }
while (line_len > 0) { while (line_len > 0) {
error = git_pkt_parse_line(&pkt, line, &line_end, line_len); error = git_pkt_parse_line(&pkt, &line_end, line, line_len);
if (error == GIT_EBUFS) { if (error == GIT_EBUFS) {
/* Buffer the data when the inner packet is split /* Buffer the data when the inner packet is split
...@@ -795,8 +794,8 @@ static int parse_report(transport_smart *transport, git_push *push) ...@@ -795,8 +794,8 @@ static int parse_report(transport_smart *transport, git_push *push)
for (;;) { for (;;) {
if (buf->offset > 0) if (buf->offset > 0)
error = git_pkt_parse_line(&pkt, buf->data, error = git_pkt_parse_line(&pkt, &line_end,
&line_end, buf->offset); buf->data, buf->offset);
else else
error = GIT_EBUFS; error = GIT_EBUFS;
......
...@@ -35,6 +35,8 @@ void test_config_include__absolute(void) ...@@ -35,6 +35,8 @@ void test_config_include__absolute(void)
cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar.baz")); cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar.baz"));
cl_assert_equal_s("huzzah", git_buf_cstr(&buf)); cl_assert_equal_s("huzzah", git_buf_cstr(&buf));
cl_git_pass(p_unlink("config-include-absolute"));
} }
void test_config_include__homedir(void) void test_config_include__homedir(void)
...@@ -48,6 +50,8 @@ void test_config_include__homedir(void) ...@@ -48,6 +50,8 @@ void test_config_include__homedir(void)
cl_assert_equal_s("huzzah", git_buf_cstr(&buf)); cl_assert_equal_s("huzzah", git_buf_cstr(&buf));
cl_sandbox_set_search_path_defaults(); cl_sandbox_set_search_path_defaults();
cl_git_pass(p_unlink("config-include-homedir"));
} }
/* We need to pretend that the variables were defined where the file was included */ /* We need to pretend that the variables were defined where the file was included */
...@@ -66,6 +70,9 @@ void test_config_include__ordering(void) ...@@ -66,6 +70,9 @@ void test_config_include__ordering(void)
git_buf_clear(&buf); git_buf_clear(&buf);
cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar.baz")); cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar.baz"));
cl_assert_equal_s("huzzah", git_buf_cstr(&buf)); cl_assert_equal_s("huzzah", git_buf_cstr(&buf));
cl_git_pass(p_unlink("included"));
cl_git_pass(p_unlink("including"));
} }
/* We need to pretend that the variables were defined where the file was included */ /* We need to pretend that the variables were defined where the file was included */
...@@ -76,8 +83,18 @@ void test_config_include__depth(void) ...@@ -76,8 +83,18 @@ void test_config_include__depth(void)
cl_git_fail(git_config_open_ondisk(&cfg, "a")); cl_git_fail(git_config_open_ondisk(&cfg, "a"));
p_unlink("a"); cl_git_pass(p_unlink("a"));
p_unlink("b"); cl_git_pass(p_unlink("b"));
}
void test_config_include__empty_path_sanely_handled(void)
{
cl_git_mkfile("a", "[include]\npath");
cl_git_pass(git_config_open_ondisk(&cfg, "a"));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "include.path"));
cl_assert_equal_s("", git_buf_cstr(&buf));
cl_git_pass(p_unlink("a"));
} }
void test_config_include__missing(void) void test_config_include__missing(void)
...@@ -89,6 +106,8 @@ void test_config_include__missing(void) ...@@ -89,6 +106,8 @@ void test_config_include__missing(void)
cl_assert(giterr_last() == NULL); cl_assert(giterr_last() == NULL);
cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar")); cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar"));
cl_assert_equal_s("baz", git_buf_cstr(&buf)); cl_assert_equal_s("baz", git_buf_cstr(&buf));
cl_git_pass(p_unlink("including"));
} }
void test_config_include__missing_homedir(void) void test_config_include__missing_homedir(void)
...@@ -103,6 +122,7 @@ void test_config_include__missing_homedir(void) ...@@ -103,6 +122,7 @@ void test_config_include__missing_homedir(void)
cl_assert_equal_s("baz", git_buf_cstr(&buf)); cl_assert_equal_s("baz", git_buf_cstr(&buf));
cl_sandbox_set_search_path_defaults(); cl_sandbox_set_search_path_defaults();
cl_git_pass(p_unlink("including"));
} }
#define replicate10(s) s s s s s s s s s s #define replicate10(s) s s s s s s s s s s
...@@ -122,6 +142,10 @@ void test_config_include__depth2(void) ...@@ -122,6 +142,10 @@ void test_config_include__depth2(void)
git_buf_clear(&buf); git_buf_clear(&buf);
cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar2")); cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar2"));
cl_assert_equal_s("baz2", git_buf_cstr(&buf)); cl_assert_equal_s("baz2", git_buf_cstr(&buf));
cl_git_pass(p_unlink("top-level"));
cl_git_pass(p_unlink("middle"));
cl_git_pass(p_unlink("bottom"));
} }
void test_config_include__removing_include_removes_values(void) void test_config_include__removing_include_removes_values(void)
...@@ -132,6 +156,9 @@ void test_config_include__removing_include_removes_values(void) ...@@ -132,6 +156,9 @@ void test_config_include__removing_include_removes_values(void)
cl_git_pass(git_config_open_ondisk(&cfg, "top-level")); cl_git_pass(git_config_open_ondisk(&cfg, "top-level"));
cl_git_mkfile("top-level", ""); cl_git_mkfile("top-level", "");
cl_git_fail(git_config_get_string_buf(&buf, cfg, "foo.bar")); cl_git_fail(git_config_get_string_buf(&buf, cfg, "foo.bar"));
cl_git_pass(p_unlink("top-level"));
cl_git_pass(p_unlink("included"));
} }
void test_config_include__rewriting_include_refreshes_values(void) void test_config_include__rewriting_include_refreshes_values(void)
...@@ -145,6 +172,10 @@ void test_config_include__rewriting_include_refreshes_values(void) ...@@ -145,6 +172,10 @@ void test_config_include__rewriting_include_refreshes_values(void)
cl_git_fail(git_config_get_string_buf(&buf, cfg, "foo.bar")); cl_git_fail(git_config_get_string_buf(&buf, cfg, "foo.bar"));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "first.other")); cl_git_pass(git_config_get_string_buf(&buf, cfg, "first.other"));
cl_assert_equal_s(buf.ptr, "value"); cl_assert_equal_s(buf.ptr, "value");
cl_git_pass(p_unlink("top-level"));
cl_git_pass(p_unlink("first"));
cl_git_pass(p_unlink("second"));
} }
void test_config_include__included_variables_cannot_be_deleted(void) void test_config_include__included_variables_cannot_be_deleted(void)
...@@ -154,13 +185,20 @@ void test_config_include__included_variables_cannot_be_deleted(void) ...@@ -154,13 +185,20 @@ void test_config_include__included_variables_cannot_be_deleted(void)
cl_git_pass(git_config_open_ondisk(&cfg, "top-level")); cl_git_pass(git_config_open_ondisk(&cfg, "top-level"));
cl_git_fail(git_config_delete_entry(cfg, "foo.bar")); cl_git_fail(git_config_delete_entry(cfg, "foo.bar"));
cl_git_pass(p_unlink("top-level"));
cl_git_pass(p_unlink("included"));
} }
void test_config_include__included_variables_cannot_be_modified(void) void test_config_include__included_variables_cannot_be_modified(void)
{ {
cl_git_mkfile("top-level", "[include]\npath = included\n"); cl_git_mkfile("top-level", "[include]\npath = included\n");
cl_git_mkfile("included", "[foo]\nbar = value"); cl_git_mkfile("included", "[foo]\nbar = value");
cl_git_pass(git_config_open_ondisk(&cfg, "top-level")); cl_git_pass(git_config_open_ondisk(&cfg, "top-level"));
cl_git_fail(git_config_set_string(cfg, "foo.bar", "other-value")); cl_git_fail(git_config_set_string(cfg, "foo.bar", "other-value"));
cl_git_pass(p_unlink("top-level"));
cl_git_pass(p_unlink("included"));
} }
#include "clar_libgit2.h"
#include "posix.h"
#include "path.h"
#include "submodule_helpers.h"
#include "fileops.h"
#include "repository.h"
static git_repository *g_repo = NULL;
void test_submodule_inject_option__initialize(void)
{
g_repo = setup_fixture_submodule_simple();
}
void test_submodule_inject_option__cleanup(void)
{
cl_git_sandbox_cleanup();
}
static int find_naughty(git_submodule *sm, const char *name, void *payload)
{
int *foundit = (int *) payload;
GIT_UNUSED(sm);
if (!git__strcmp("naughty", name))
*foundit = true;
return 0;
}
void test_submodule_inject_option__url(void)
{
int foundit;
git_submodule *sm;
git_buf buf = GIT_BUF_INIT;
cl_git_pass(git_buf_joinpath(&buf, git_repository_workdir(g_repo), ".gitmodules"));
cl_git_rewritefile(buf.ptr,
"[submodule \"naughty\"]\n"
" path = testrepo\n"
" url = -u./payload\n");
git_buf_free(&buf);
/* We do want to find it, but with the appropriate field empty */
foundit = 0;
cl_git_pass(git_submodule_foreach(g_repo, find_naughty, &foundit));
cl_assert_equal_i(1, foundit);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "naughty"));
cl_assert_equal_s("testrepo", git_submodule_path(sm));
cl_assert_equal_p(NULL, git_submodule_url(sm));
git_submodule_free(sm);
}
void test_submodule_inject_option__path(void)
{
int foundit;
git_submodule *sm;
git_buf buf = GIT_BUF_INIT;
cl_git_pass(git_buf_joinpath(&buf, git_repository_workdir(g_repo), ".gitmodules"));
cl_git_rewritefile(buf.ptr,
"[submodule \"naughty\"]\n"
" path = --something\n"
" url = blah.git\n");
git_buf_free(&buf);
/* We do want to find it, but with the appropriate field empty */
foundit = 0;
cl_git_pass(git_submodule_foreach(g_repo, find_naughty, &foundit));
cl_assert_equal_i(1, foundit);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "naughty"));
cl_assert_equal_s("naughty", git_submodule_path(sm));
cl_assert_equal_s("blah.git", git_submodule_url(sm));
git_submodule_free(sm);
}
#include "clar_libgit2.h"
#include "transports/smart.h"
enum expected_status {
PARSE_SUCCESS,
PARSE_FAILURE
};
static void assert_flush_parses(const char *line)
{
size_t linelen = strlen(line) + 1;
const char *endptr;
git_pkt *pkt;
cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen));
cl_assert_equal_i(pkt->type, GIT_PKT_FLUSH);
cl_assert_equal_strn(endptr, line + 4, linelen - 4);
git_pkt_free((git_pkt *) pkt);
}
static void assert_data_pkt_parses(const char *line, const char *expected_data, size_t expected_len)
{
size_t linelen = strlen(line) + 1;
const char *endptr;
git_pkt_data *pkt;
cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen));
cl_assert_equal_i(pkt->type, GIT_PKT_DATA);
cl_assert_equal_i(pkt->len, expected_len);
cl_assert_equal_strn(pkt->data, expected_data, expected_len);
git_pkt_free((git_pkt *) pkt);
}
static void assert_sideband_progress_parses(const char *line, const char *expected_data, size_t expected_len)
{
size_t linelen = strlen(line) + 1;
const char *endptr;
git_pkt_progress *pkt;
cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen));
cl_assert_equal_i(pkt->type, GIT_PKT_PROGRESS);
cl_assert_equal_i(pkt->len, expected_len);
cl_assert_equal_strn(pkt->data, expected_data, expected_len);
git_pkt_free((git_pkt *) pkt);
}
static void assert_error_parses(const char *line, const char *expected_error, size_t expected_len)
{
size_t linelen = strlen(line) + 1;
const char *endptr;
git_pkt_err *pkt;
cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen));
cl_assert_equal_i(pkt->type, GIT_PKT_ERR);
cl_assert_equal_i(pkt->len, expected_len);
cl_assert_equal_strn(pkt->error, expected_error, expected_len);
git_pkt_free((git_pkt *) pkt);
}
static void assert_ack_parses(const char *line, const char *expected_oid, enum git_ack_status expected_status)
{
size_t linelen = strlen(line) + 1;
const char *endptr;
git_pkt_ack *pkt;
git_oid oid;
cl_git_pass(git_oid_fromstr(&oid, expected_oid));
cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen));
cl_assert_equal_i(pkt->type, GIT_PKT_ACK);
cl_assert_equal_oid(&pkt->oid, &oid);
cl_assert_equal_i(pkt->status, expected_status);
git_pkt_free((git_pkt *) pkt);
}
static void assert_nak_parses(const char *line)
{
size_t linelen = strlen(line) + 1;
const char *endptr;
git_pkt *pkt;
cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen));
cl_assert_equal_i(pkt->type, GIT_PKT_NAK);
cl_assert_equal_strn(endptr, line + 7, linelen - 7);
git_pkt_free((git_pkt *) pkt);
}
static void assert_comment_parses(const char *line, const char *expected_comment)
{
size_t linelen = strlen(line) + 1;
const char *endptr;
git_pkt_comment *pkt;
cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen));
cl_assert_equal_i(pkt->type, GIT_PKT_COMMENT);
cl_assert_equal_strn(pkt->comment, expected_comment, strlen(expected_comment));
git_pkt_free((git_pkt *) pkt);
}
static void assert_ok_parses(const char *line, const char *expected_ref)
{
size_t linelen = strlen(line) + 1;
const char *endptr;
git_pkt_ok *pkt;
cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen));
cl_assert_equal_i(pkt->type, GIT_PKT_OK);
cl_assert_equal_strn(pkt->ref, expected_ref, strlen(expected_ref));
git_pkt_free((git_pkt *) pkt);
}
static void assert_unpack_parses(const char *line, bool ok)
{
size_t linelen = strlen(line) + 1;
const char *endptr;
git_pkt_unpack *pkt;
cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen));
cl_assert_equal_i(pkt->type, GIT_PKT_UNPACK);
cl_assert_equal_i(pkt->unpack_ok, ok);
git_pkt_free((git_pkt *) pkt);
}
static void assert_ng_parses(const char *line, const char *expected_ref, const char *expected_msg)
{
size_t linelen = strlen(line) + 1;
const char *endptr;
git_pkt_ng *pkt;
cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen));
cl_assert_equal_i(pkt->type, GIT_PKT_NG);
cl_assert_equal_strn(pkt->ref, expected_ref, strlen(expected_ref));
cl_assert_equal_strn(pkt->msg, expected_msg, strlen(expected_msg));
git_pkt_free((git_pkt *) pkt);
}
#define assert_ref_parses(line, expected_oid, expected_ref, expected_capabilities) \
assert_ref_parses_(line, sizeof(line), expected_oid, expected_ref, expected_capabilities)
static void assert_ref_parses_(const char *line, size_t linelen, const char *expected_oid,
const char *expected_ref, const char *expected_capabilities)
{
const char *endptr;
git_pkt_ref *pkt;
git_oid oid;
cl_git_pass(git_oid_fromstr(&oid, expected_oid));
cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen));
cl_assert_equal_i(pkt->type, GIT_PKT_REF);
cl_assert_equal_oid(&pkt->head.oid, &oid);
cl_assert_equal_strn(pkt->head.name, expected_ref, strlen(expected_ref));
if (expected_capabilities)
cl_assert_equal_strn(pkt->capabilities, expected_capabilities, strlen(expected_capabilities));
else
cl_assert_equal_p(NULL, pkt->capabilities);
git_pkt_free((git_pkt *) pkt);
}
static void assert_pkt_fails(const char *line)
{
const char *endptr;
git_pkt *pkt;
cl_git_fail(git_pkt_parse_line(&pkt, &endptr, line, strlen(line) + 1));
}
void test_transports_smart_packet__parsing_garbage_fails(void)
{
assert_pkt_fails("0foobar");
assert_pkt_fails("00foobar");
assert_pkt_fails("000foobar");
assert_pkt_fails("0001");
assert_pkt_fails("");
assert_pkt_fails("0");
assert_pkt_fails("0i00");
assert_pkt_fails("f");
}
void test_transports_smart_packet__flush_parses(void)
{
assert_flush_parses("0000");
assert_flush_parses("0000foobar");
}
void test_transports_smart_packet__data_pkt(void)
{
assert_pkt_fails("000foobar");
assert_pkt_fails("0001o");
assert_pkt_fails("0001\1");
assert_data_pkt_parses("0005\1", "", 0);
assert_pkt_fails("0009\1o");
assert_data_pkt_parses("0009\1data", "data", 4);
assert_data_pkt_parses("000a\1data", "data", 5);
}
void test_transports_smart_packet__sideband_progress_pkt(void)
{
assert_pkt_fails("0001\2");
assert_sideband_progress_parses("0005\2", "", 0);
assert_pkt_fails("0009\2o");
assert_sideband_progress_parses("0009\2data", "data", 4);
assert_sideband_progress_parses("000a\2data", "data", 5);
}
void test_transports_smart_packet__sideband_err_pkt(void)
{
assert_pkt_fails("0001\3");
assert_error_parses("0005\3", "", 0);
assert_pkt_fails("0009\3o");
assert_error_parses("0009\3data", "data", 4);
assert_error_parses("000a\3data", "data", 5);
}
void test_transports_smart_packet__ack_pkt(void)
{
assert_ack_parses("0030ACK 0000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000", 0);
assert_ack_parses("0039ACK 0000000000000000000000000000000000000000 continue",
"0000000000000000000000000000000000000000",
GIT_ACK_CONTINUE);
assert_ack_parses("0037ACK 0000000000000000000000000000000000000000 common",
"0000000000000000000000000000000000000000",
GIT_ACK_COMMON);
assert_ack_parses("0037ACK 0000000000000000000000000000000000000000 ready",
"0000000000000000000000000000000000000000",
GIT_ACK_READY);
/* these should fail as they don't have OIDs */
assert_pkt_fails("0007ACK");
assert_pkt_fails("0008ACK ");
/* this one is missing a space and should thus fail */
assert_pkt_fails("0036ACK00000000000000000x0000000000000000000000 ready");
/* the following ones have invalid OIDs and should thus fail */
assert_pkt_fails("0037ACK 00000000000000000x0000000000000000000000 ready");
assert_pkt_fails("0036ACK 000000000000000000000000000000000000000 ready");
assert_pkt_fails("0036ACK 00000000000000000x0000000000000000000000ready");
/* this one has an invalid status and should thus fail */
assert_pkt_fails("0036ACK 0000000000000000000000000000000000000000 read");
}
void test_transports_smart_packet__nak_pkt(void)
{
assert_nak_parses("0007NAK");
assert_pkt_fails("0007NaK");
assert_pkt_fails("0007nak");
assert_nak_parses("0007NAKfoobar");
assert_pkt_fails("0007nakfoobar");
assert_pkt_fails("0007 NAK");
}
void test_transports_smart_packet__error_pkt(void)
{
assert_pkt_fails("0007ERR");
assert_pkt_fails("0008ERRx");
assert_error_parses("0008ERR ", "", 0);
assert_error_parses("000EERR ERRMSG", "ERRMSG", 6);
}
void test_transports_smart_packet__comment_pkt(void)
{
assert_comment_parses("0005#", "");
assert_comment_parses("000B#foobar", "#fooba");
assert_comment_parses("000C#foobar", "#foobar");
assert_comment_parses("001A#this is a comment\nfoo", "#this is a comment\nfoo");
}
void test_transports_smart_packet__ok_pkt(void)
{
assert_pkt_fails("0007ok\n");
assert_ok_parses("0007ok ", "");
assert_ok_parses("0008ok \n", "");
assert_ok_parses("0008ok x", "x");
assert_ok_parses("0009ok x\n", "x");
assert_pkt_fails("001OK ref/foo/bar");
assert_ok_parses("0012ok ref/foo/bar", "ref/foo/bar");
assert_pkt_fails("0013OK ref/foo/bar\n");
assert_ok_parses("0013ok ref/foo/bar\n", "ref/foo/bar");
}
void test_transports_smart_packet__ng_pkt(void)
{
/* TODO: same as for ok pkt */
assert_pkt_fails("0007ng\n");
assert_pkt_fails("0008ng \n");
assert_pkt_fails("000Bng ref\n");
assert_pkt_fails("000Bng ref\n");
/* TODO: is this a valid packet line? Probably not. */
assert_ng_parses("000Ang x\n", "", "x");
assert_ng_parses("000Fng ref msg\n", "ref", "msg");
assert_ng_parses("000Fng ref msg\n", "ref", "msg");
}
void test_transports_smart_packet__unpack_pkt(void)
{
assert_unpack_parses("000Dunpack ok", 1);
assert_unpack_parses("000Dunpack ng error-msg", 0);
/* TODO: the following tests should fail */
assert_unpack_parses("000Aunpack", 0);
assert_unpack_parses("0011unpack foobar", 0);
assert_unpack_parses("0010unpack ng ok", 0);
assert_unpack_parses("0010unpack okfoo", 1);
}
void test_transports_smart_packet__ref_pkt(void)
{
assert_pkt_fails("002C0000000000000000000000000000000000000000");
assert_pkt_fails("002D0000000000000000000000000000000000000000\n");
assert_pkt_fails("00300000000000000000000000000000000000000000HEAD");
assert_pkt_fails("004800000000x0000000000000000000000000000000 refs/heads/master\0multi_ack");
assert_ref_parses(
"003F0000000000000000000000000000000000000000 refs/heads/master\0",
"0000000000000000000000000000000000000000", "refs/heads/master", "");
assert_ref_parses(
"00480000000000000000000000000000000000000000 refs/heads/master\0multi_ack",
"0000000000000000000000000000000000000000", "refs/heads/master", "multi_ack");
assert_ref_parses(
"00460000000000000000000000000000000000000000 refs/heads/master\0one two",
"0000000000000000000000000000000000000000", "refs/heads/master", "one two");
assert_ref_parses(
"00310000000000000000000000000000000000000000 HEAD",
"0000000000000000000000000000000000000000", "HEAD", NULL);
assert_pkt_fails("0031000000000000000000000000000000000000000 HEAD");
assert_ref_parses(
"00360000000000000000000000000000000000000000 HEAD HEAD",
"0000000000000000000000000000000000000000", "HEAD HEAD", NULL);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment