Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ PHP NEWS
- GD:
. Fixed imageaffinematrixget() and imageaffinematrixconcat() reporting the
wrong argument in error messages. (Weilin Du)
. Fixed bug GH-23457 (imagebmp() is extremely slow when writing to a file).
(Lazizbek Ergashev)

- Intl:
. Fixed a double-free when IntlGregorianCalendar construction fails after
Expand Down
28 changes: 24 additions & 4 deletions ext/gd/gd.c
Original file line number Diff line number Diff line change
Expand Up @@ -4464,21 +4464,39 @@ static void _php_image_output_ctxfree(struct gdIOCtx *ctx) /* {{{ */
efree(ctx);
} /* }}} */

typedef struct {
gdIOCtx ctx;
size_t buf_len;
unsigned char buf[8192];
} php_gd_stream_ctx;

static void _php_image_stream_flush(php_gd_stream_ctx *stream_ctx) /* {{{ */
{
if (stream_ctx->buf_len) {
php_stream_write((php_stream *) stream_ctx->ctx.data, (char *) stream_ctx->buf, stream_ctx->buf_len);
stream_ctx->buf_len = 0;
}
} /* }}} */

static void _php_image_stream_putc(struct gdIOCtx *ctx, int c) /* {{{ */ {
char ch = (char) c;
php_stream * stream = (php_stream *)ctx->data;
php_stream_write(stream, &ch, 1);
php_gd_stream_ctx *stream_ctx = (php_gd_stream_ctx *) ctx;
if (stream_ctx->buf_len == sizeof(stream_ctx->buf)) {
_php_image_stream_flush(stream_ctx);
}
stream_ctx->buf[stream_ctx->buf_len++] = (unsigned char) c;
} /* }}} */

static int _php_image_stream_putbuf(struct gdIOCtx *ctx, const void* buf, int l) /* {{{ */
{
php_stream * stream = (php_stream *)ctx->data;
_php_image_stream_flush((php_gd_stream_ctx *) ctx);
return php_stream_write(stream, (void *)buf, l);
} /* }}} */

static void _php_image_stream_ctxfree(struct gdIOCtx *ctx) /* {{{ */
{
if(ctx->data) {
_php_image_stream_flush((php_gd_stream_ctx *) ctx);
ctx->data = NULL;
}
efree(ctx);
Expand All @@ -4487,14 +4505,16 @@ static void _php_image_stream_ctxfree(struct gdIOCtx *ctx) /* {{{ */
static void _php_image_stream_ctxfreeandclose(struct gdIOCtx *ctx) /* {{{ */
{
if(ctx->data) {
_php_image_stream_flush((php_gd_stream_ctx *) ctx);
php_stream_close((php_stream *) ctx->data);
ctx->data = NULL;
}
efree(ctx);
} /* }}} */

static gdIOCtx *create_stream_context(php_stream *stream, int close_stream) {
gdIOCtx *ctx = ecalloc(1, sizeof(gdIOCtx));
php_gd_stream_ctx *stream_ctx = ecalloc(1, sizeof(php_gd_stream_ctx));
gdIOCtx *ctx = &stream_ctx->ctx;

ctx->putC = _php_image_stream_putc;
ctx->putBuf = _php_image_stream_putbuf;
Expand Down
37 changes: 37 additions & 0 deletions ext/gd/tests/gh23457.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
--TEST--
GH-23457 (imagebmp() writes to the stream one byte at a time)
--EXTENSIONS--
gd
--FILE--
<?php
class write_counter
{
public $context;

public static int $writes = 0;

public function stream_open(string $path, string $mode, int $options, ?string &$opened_path): bool
{
return true;
}

public function stream_write(string $data): int
{
self::$writes++;
return strlen($data);
}

public function stream_close(): void
{
}
}

stream_wrapper_register('gh23457', write_counter::class);

$im = imagecreatetruecolor(200, 200);
var_dump(imagebmp($im, 'gh23457://image.bmp'));
var_dump(write_counter::$writes < 100);
?>
--EXPECT--
bool(true)
bool(true)
Loading