[PECL-CVS] [pecl-text-xdiff] main: Merge pull request #10 from php/fix-memory-issues
[email protected] (Rasmus Lerdorf via GitHub) Mon, 6 Apr 2026 09:39:32 +0000
| Newsgroups | php.pecl.cvs |
|---|---|
| Message-ID | <[email protected]> |
Author: Rasmus Lerdorf (rlerdorf)
Committer: GitHub (web-flow)
Pusher: rlerdorf
Date: 2026-04-06T10:39:29+01:00
Commit: https://github.com/php/pecl-text-xdiff/commit/db90b2d8751a9c66ee806d161dfdb3f3ccfab09e
Raw diff: https://github.com/php/pecl-text-xdiff/commit/db90b2d8751a9c66ee806d161dfdb3f3ccfab09e.diff
Merge pull request #10 from php/fix-memory-issues
memory safety cleanup
Changed paths:
A tests/file_bpatch_retval.phpt
M xdiff.c
Diff:
diff --git a/tests/file_bpatch_retval.phpt b/tests/file_bpatch_retval.phpt
new file mode 100644
index 0000000..e0f8eda
--- /dev/null
+++ b/tests/file_bpatch_retval.phpt
@@ -0,0 +1,21 @@
+--TEST--
+xdiff_file_bpatch() return value on success and failure
+--SKIPIF--
+<?php if (!extension_loaded("xdiff")) print "skip"; ?>
+--FILE--
+<?php
+$result = xdiff_file_bpatch(__DIR__ . '/file.1', __DIR__ . '/file.bdiff', __DIR__ . '/file.bpatch_out');
+var_dump($result);
+$a = file_get_contents(__DIR__ . '/file.2');
+$b = file_get_contents(__DIR__ . '/file.bpatch_out');
+echo "content match: " . (strcmp($a, $b) === 0 ? "yes" : "no") . "\n";
+unlink(__DIR__ . '/file.bpatch_out');
+
+$result = xdiff_file_bpatch(__DIR__ . '/file.1', __DIR__ . '/file.1', __DIR__ . '/file.bpatch_out2');
+var_dump($result);
+@unlink(__DIR__ . '/file.bpatch_out2');
+?>
+--EXPECT--
+bool(true)
+content match: yes
+bool(false)
diff --git a/xdiff.c b/xdiff.c
index 9b0f8c3..72423fe 100644
--- a/xdiff.c
+++ b/xdiff.c
@@ -71,28 +71,28 @@ extern char libxdiff_version[];
struct string_buffer {
char *ptr;
- unsigned long size;
+ size_t size;
};
static int load_mm_file(const char *filepath, mmfile_t *dest);
-static int load_into_mm_file(const char *buffer, unsigned long size, mmfile_t *dest);
+static int load_into_mm_file(const char *buffer, size_t size, mmfile_t *dest);
static int append_string(void *ptr, mmbuffer_t *buffer, int array_size);
static int append_stream(void *ptr, mmbuffer_t *buffer, int array_size);
static int init_string(struct string_buffer *string);
static void free_string(struct string_buffer *string);
static int make_diff(char *filepath1, char *filepath2, xdemitcb_t *output, int context, int minimal);
-static int make_diff_str(char *str1, int size1, char *str2, int size2, xdemitcb_t *output, int context, int minimal);
+static int make_diff_str(char *str1, size_t size1, char *str2, size_t size2, xdemitcb_t *output, int context, int minimal);
static int make_bdiff(char *filepath1, char *filepath2, xdemitcb_t *output);
-static int make_bdiff_str(char *str1, int size1, char *str2, int size2, xdemitcb_t *output);
+static int make_bdiff_str(char *str1, size_t size1, char *str2, size_t size2, xdemitcb_t *output);
static int make_patch(char *file_path, char *patch_path, xdemitcb_t *output, xdemitcb_t *error, int flags);
-static int make_patch_str(char *file, int size1, char *patch, int size2, xdemitcb_t *output, xdemitcb_t *error, int flags);
+static int make_patch_str(char *file, size_t size1, char *patch, size_t size2, xdemitcb_t *output, xdemitcb_t *error, int flags);
static int make_bpatch(char *file_path, char *patch_path, xdemitcb_t *output);
-static int make_bpatch_str(char *file, int size1, char *patch, int size2, xdemitcb_t *output);
+static int make_bpatch_str(char *file, size_t size1, char *patch, size_t size2, xdemitcb_t *output);
static int make_merge3(char *filepath1, char *filepath2, char *filepath3, xdemitcb_t *output, xdemitcb_t *error);
-static int make_merge3_str(char *content1, int size1, char *content2, int size2, char *content3, int size3, xdemitcb_t *output, xdemitcb_t *error);
+static int make_merge3_str(char *content1, size_t size1, char *content2, size_t size2, char *content3, size_t size3, xdemitcb_t *output, xdemitcb_t *error);
static int make_rabdiff(char *filepath1, char *filepath2, xdemitcb_t *output);
-static int make_rabdiff_str(char *str1, int size1, char *str2, int size2, xdemitcb_t *output);
+static int make_rabdiff_str(char *str1, size_t size1, char *str2, size_t size2, xdemitcb_t *output);
static void *xdiff_malloc(void *foo, unsigned int size)
{
@@ -187,8 +187,13 @@ PHP_FUNCTION(xdiff_string_diff)
output.priv= &string;
output.outf = append_string;
- make_diff_str(str1->val, str1->len, str2->val, str2->len, &output, context, minimal);
+ retval = make_diff_str(str1->val, str1->len, str2->val, str2->len, &output, context, minimal);
+ if (!retval)
+ goto out_free_string;
+
RETVAL_STRINGL(string.ptr, string.size);
+
+out_free_string:
free_string(&string);
out:
return;
@@ -254,10 +259,14 @@ PHP_FUNCTION(xdiff_string_bdiff)
output.priv= &string;
output.outf = append_string;
- make_bdiff_str(str1->val, str1->len, str2->val, str2->len, &output);
+ retval = make_bdiff_str(str1->val, str1->len, str2->val, str2->len, &output);
+ if (!retval)
+ goto out_free_string;
+
RETVAL_STRINGL(string.ptr, string.size);
- free_string(&string);
+out_free_string:
+ free_string(&string);
out:
return;
}
@@ -320,10 +329,14 @@ PHP_FUNCTION(xdiff_string_rabdiff)
output.priv= &string;
output.outf = append_string;
- make_rabdiff_str(str1->val, str1->len, str2->val, str2->len, &output);
+ retval = make_rabdiff_str(str1->val, str1->len, str2->val, str2->len, &output);
+ if (!retval)
+ goto out_free_string;
+
RETVAL_STRINGL(string.ptr, string.size);
- free_string(&string);
+out_free_string:
+ free_string(&string);
out:
return;
}
@@ -557,7 +570,7 @@ PHP_FUNCTION(xdiff_file_bpatch)
retval = make_bpatch(src_path->val, patch_path->val, &output);
php_stream_close(output_stream);
- if (retval == 0)
+ if (retval)
RETVAL_TRUE;
out:
@@ -719,8 +732,10 @@ static int load_mm_file(const char *filepath, mmfile_t *dest)
goto out_stream_close;
filesize = stat.sb.st_size;
+ if (filesize < 0 || filesize > LONG_MAX)
+ goto out_stream_close;
- retval = xdl_init_mmfile(dest, filesize, XDL_MMF_ATOMIC);
+ retval = xdl_init_mmfile(dest, (long) filesize, XDL_MMF_ATOMIC);
if (retval < 0)
goto out_stream_close;
@@ -728,7 +743,9 @@ static int load_mm_file(const char *filepath, mmfile_t *dest)
if (!ptr)
goto out_free_mmfile;
- php_stream_read(src, ptr, filesize);
+ if (php_stream_read(src, ptr, filesize) != filesize)
+ goto out_free_mmfile;
+
php_stream_close(src);
return 1;
@@ -741,12 +758,15 @@ static int load_mm_file(const char *filepath, mmfile_t *dest)
return 0;
}
-static int load_into_mm_file(const char *buffer, unsigned long size, mmfile_t *dest)
+static int load_into_mm_file(const char *buffer, size_t size, mmfile_t *dest)
{
int retval;
void *ptr;
- retval = xdl_init_mmfile(dest, size, XDL_MMF_ATOMIC);
+ if (size > LONG_MAX)
+ goto out;
+
+ retval = xdl_init_mmfile(dest, (long) size, XDL_MMF_ATOMIC);
if (retval < 0)
goto out;
@@ -767,12 +787,21 @@ static int append_string(void *ptr, mmbuffer_t *buffer, int array_size)
{
struct string_buffer *string = ptr;
void *new_ptr;
- unsigned int i;
+ int i;
+
+ if (array_size <= 0)
+ return 0;
for (i = 0; i < array_size; i++) {
+ if (buffer[i].size < 0 || (size_t) buffer[i].size > SIZE_MAX - string->size - 1) {
+ efree(string->ptr);
+ string->ptr = NULL;
+ return -1;
+ }
new_ptr = erealloc(string->ptr, string->size + buffer[i].size + 1);
if (!new_ptr) {
efree(string->ptr);
+ string->ptr = NULL;
return -1;
}
@@ -780,9 +809,7 @@ static int append_string(void *ptr, mmbuffer_t *buffer, int array_size)
memcpy(string->ptr + string->size, buffer[i].ptr, buffer[i].size);
string->size += buffer[i].size;
}
- if (array_size) {
- string->ptr[string->size] = '\0';
- }
+ string->ptr[string->size] = '\0';
return 0;
}
@@ -790,7 +817,10 @@ static int append_string(void *ptr, mmbuffer_t *buffer, int array_size)
static int append_stream(void *ptr, mmbuffer_t *buffer, int array_size)
{
php_stream *stream = ptr;
- unsigned int i;
+ int i;
+
+ if (array_size <= 0)
+ return 1;
for (i = 0; i < array_size; i++) {
php_stream_write(stream, buffer[i].ptr, buffer[i].size);
@@ -849,7 +879,7 @@ static int make_diff(char *filepath1, char *filepath2, xdemitcb_t *output, int c
return result;
}
-static int make_diff_str(char *str1, int size1, char *str2, int size2, xdemitcb_t *output, int context, int minimal)
+static int make_diff_str(char *str1, size_t size1, char *str2, size_t size2, xdemitcb_t *output, int context, int minimal)
{
mmfile_t file1, file2;
xpparam_t params;
@@ -911,7 +941,7 @@ static int make_bdiff(char *filepath1, char *filepath2, xdemitcb_t *output)
return result;
}
-static int make_bdiff_str(char *str1, int size1, char *str2, int size2, xdemitcb_t *output)
+static int make_bdiff_str(char *str1, size_t size1, char *str2, size_t size2, xdemitcb_t *output)
{
mmfile_t file1, file2;
bdiffparam_t params;
@@ -968,7 +998,7 @@ static int make_rabdiff(char *filepath1, char *filepath2, xdemitcb_t *output)
return result;
}
-static int make_rabdiff_str(char *str1, int size1, char *str2, int size2, xdemitcb_t *output)
+static int make_rabdiff_str(char *str1, size_t size1, char *str2, size_t size2, xdemitcb_t *output)
{
mmfile_t file1, file2;
int retval, result = 0;
@@ -1022,7 +1052,7 @@ static int make_patch(char *file_path, char *patch_path, xdemitcb_t *output, xde
return result;
}
-static int make_patch_str(char *file, int size1, char *patch, int size2, xdemitcb_t *output, xdemitcb_t *error, int flags)
+static int make_patch_str(char *file, size_t size1, char *patch, size_t size2, xdemitcb_t *output, xdemitcb_t *error, int flags)
{
mmfile_t file_mm, patch_mm;
int retval, result = 0;
@@ -1076,7 +1106,7 @@ static int make_bpatch(char *file_path, char *patch_path, xdemitcb_t *output)
return result;
}
-static int make_bpatch_str(char *file, int size1, char *patch, int size2, xdemitcb_t *output)
+static int make_bpatch_str(char *file, size_t size1, char *patch, size_t size2, xdemitcb_t *output)
{
mmfile_t file_mm, patch_mm;
int retval, result = 0;
@@ -1136,7 +1166,7 @@ static int make_merge3(char *filepath1, char *filepath2, char *filepath3, xdemit
return result;
}
-static int make_merge3_str(char *content1, int size1, char *content2, int size2, char *content3, int size3, xdemitcb_t *output, xdemitcb_t *error)
+static int make_merge3_str(char *content1, size_t size1, char *content2, size_t size2, char *content3, size_t size3, xdemitcb_t *output, xdemitcb_t *error)
{
mmfile_t file1, file2, file3;
int retval, result = 0;