$OpenBSD: patch-mercurial_cext_manifest_c,v 1.1 2018/10/02 14:04:01 juanfra Exp $

Index: mercurial/cext/manifest.c
--- mercurial/cext/manifest.c.orig
+++ mercurial/cext/manifest.c
@@ -42,7 +42,8 @@ typedef struct {
 /* get the length of the path for a line */
 static size_t pathlen(line *l)
 {
-	return strlen(l->start);
+	const char *end = memchr(l->start, '\0', l->len);
+	return (end) ? (size_t)(end - l->start) : l->len;
 }
 
 /* get the node value of a single line */
@@ -50,7 +51,12 @@ static PyObject *nodeof(line *l)
 {
 	char *s = l->start;
 	ssize_t llen = pathlen(l);
-	PyObject *hash = unhexlify(s + llen + 1, 40);
+	PyObject *hash;
+	if (llen + 1 + 40 + 1 > l->len) { /* path '\0' hash '\n' */
+		PyErr_SetString(PyExc_ValueError, "manifest line too short");
+		return NULL;
+	}
+	hash = unhexlify(s + llen + 1, 40);
 	if (!hash) {
 		return NULL;
 	}
@@ -135,12 +141,22 @@ static int find_lines(lazymanifest *self, char *data, 
 	return 0;
 }
 
+static void lazymanifest_init_early(lazymanifest *self)
+{
+	self->pydata = NULL;
+	self->lines = NULL;
+	self->numlines = 0;
+	self->maxlines = 0;
+}
+
 static int lazymanifest_init(lazymanifest *self, PyObject *args)
 {
 	char *data;
 	Py_ssize_t len;
 	int err, ret;
 	PyObject *pydata;
+
+	lazymanifest_init_early(self);
 	if (!PyArg_ParseTuple(args, "S", &pydata)) {
 		return -1;
 	}
@@ -185,15 +201,13 @@ static void lazymanifest_dealloc(lazymanifest *self)
 {
 	/* free any extra lines we had to allocate */
 	int i;
-	for (i = 0; i < self->numlines; i++) {
+	for (i = 0; self->lines && (i < self->numlines); i++) {
 		if (self->lines[i].from_malloc) {
 			free(self->lines[i].start);
 		}
 	}
-	if (self->lines) {
-		free(self->lines);
-		self->lines = NULL;
-	}
+	free(self->lines);
+	self->lines = NULL;
 	if (self->pydata) {
 		Py_DECREF(self->pydata);
 		self->pydata = NULL;
@@ -240,10 +254,13 @@ static PyObject *lmiter_iterentriesnext(PyObject *o)
 	pl = pathlen(l);
 	path = PyBytes_FromStringAndSize(l->start, pl);
 	hash = nodeof(l);
+	if (!path || !hash) {
+		goto done;
+	}
 	consumed = pl + 41;
 	flags = PyBytes_FromStringAndSize(l->start + consumed,
 					   l->len - consumed - 1);
-	if (!path || !hash || !flags) {
+	if (!flags) {
 		goto done;
 	}
 	ret = PyTuple_Pack(3, path, hash, flags);
@@ -670,6 +687,7 @@ static lazymanifest *lazymanifest_copy(lazymanifest *s
 	if (!copy) {
 		goto nomem;
 	}
+	lazymanifest_init_early(copy);
 	copy->numlines = self->numlines;
 	copy->livelines = self->livelines;
 	copy->dirty = false;
@@ -707,6 +725,7 @@ static lazymanifest *lazymanifest_filtercopy(
 	if (!copy) {
 		goto nomem;
 	}
+	lazymanifest_init_early(copy);
 	copy->dirty = true;
 	copy->lines = malloc(self->maxlines * sizeof(line));
 	if (!copy->lines) {
@@ -715,21 +734,19 @@ static lazymanifest *lazymanifest_filtercopy(
 	copy->maxlines = self->maxlines;
 	copy->numlines = 0;
 	copy->pydata = self->pydata;
-	Py_INCREF(self->pydata);
+	Py_INCREF(copy->pydata);
 	for (i = 0; i < self->numlines; i++) {
 		PyObject *arglist = NULL, *result = NULL;
 		arglist = Py_BuildValue("(s)", self->lines[i].start);
 		if (!arglist) {
-			return NULL;
+			goto bail;
 		}
 		result = PyObject_CallObject(matchfn, arglist);
 		Py_DECREF(arglist);
 		/* if the callback raised an exception, just let it
 		 * through and give up */
 		if (!result) {
-			free(copy->lines);
-			Py_DECREF(self->pydata);
-			return NULL;
+			goto bail;
 		}
 		if (PyObject_IsTrue(result)) {
 			assert(!(self->lines[i].from_malloc));
@@ -741,6 +758,7 @@ static lazymanifest *lazymanifest_filtercopy(
 	return copy;
 nomem:
 	PyErr_NoMemory();
+bail:
 	Py_XDECREF(copy);
 	return NULL;
 }
