Skip to content
Merged
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
4 changes: 4 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ PHP NEWS
. Casting floats that are not representable as ints now emits a warning.
(Girgias)

- Bz2:
. Fixed bug GH-19810 (Broken bzopen() stream mode validation). (ilutov)

- Curl:
. Fix cloning of CURLOPT_POSTFIELDS when using the clone operator instead
of the curl_copy_handle() function to clone a CurlHandle. (timwolla)
Expand Down Expand Up @@ -45,6 +48,7 @@ PHP NEWS
- Opcache:
. Fixed bug GH-19669 (assertion failure in zend_jit_trace_type_to_info_ex).
(Arnaud)
. Fixed bug GH-19831 (function JIT may not deref property value). (Arnaud)

- MBstring:
. Updated Unicode data tables to Unicode 17.0. (Yuya Hamada)
Expand Down
27 changes: 19 additions & 8 deletions ext/bz2/bz2.c
Original file line number Diff line number Diff line change
Expand Up @@ -366,27 +366,38 @@ PHP_FUNCTION(bzopen)
php_stream_from_zval(stream, file);
stream_mode_len = strlen(stream->mode);

if (stream_mode_len != 1 && !(stream_mode_len == 2 && memchr(stream->mode, 'b', 2))) {
php_error_docref(NULL, E_WARNING, "Cannot use stream opened in mode '%s'", stream->mode);
RETURN_FALSE;
} else if (stream_mode_len == 1 && stream->mode[0] != 'r' && stream->mode[0] != 'w' && stream->mode[0] != 'a' && stream->mode[0] != 'x') {
char primary_stream_mode;
if (stream_mode_len == 1) {
primary_stream_mode = stream->mode[0];
} else if (stream_mode_len == 2) {
char secondary_stream_mode = 0;
if (stream->mode[0] != 'b') {
primary_stream_mode = stream->mode[0];
secondary_stream_mode = stream->mode[1];
} else {
primary_stream_mode = stream->mode[1];
secondary_stream_mode = stream->mode[0];
}
if (secondary_stream_mode != 'b') {
goto unsupported_mode;
}
} else {
unsupported_mode:
php_error_docref(NULL, E_WARNING, "Cannot use stream opened in mode '%s'", stream->mode);
RETURN_FALSE;
}

switch(mode[0]) {
case 'r':
/* only "r" and "rb" are supported */
if (stream->mode[0] != mode[0] && !(stream_mode_len == 2 && stream->mode[1] != mode[0])) {
if (primary_stream_mode != 'r') {
php_error_docref(NULL, E_WARNING, "Cannot read from a stream opened in write only mode");
RETURN_FALSE;
}
break;
case 'w':
/* support only "w"(b), "a"(b), "x"(b) */
if (stream->mode[0] != mode[0] && !(stream_mode_len == 2 && stream->mode[1] != mode[0])
&& stream->mode[0] != 'a' && !(stream_mode_len == 2 && stream->mode[1] != 'a')
&& stream->mode[0] != 'x' && !(stream_mode_len == 2 && stream->mode[1] != 'x')) {
if (!strchr("wax", primary_stream_mode)) {
php_error_docref(NULL, E_WARNING, "cannot write to a stream opened in read only mode");
RETURN_FALSE;
}
Expand Down
11 changes: 11 additions & 0 deletions ext/bz2/tests/gh19810.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--TEST--
GH-19810: bzopen() stream mode validation
--EXTENSIONS--
bz2
--FILE--
<?php
var_dump(bzopen(STDERR, 'r'));
?>
--EXPECTF--
Warning: bzopen(): Cannot read from a stream opened in write only mode in %s on line %d
bool(false)
1 change: 1 addition & 0 deletions ext/opcache/jit/ir/LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
MIT License

Copyright (c) 2022 Zend by Perforce
Copyright (c) 2025 Dmitry Stogov

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
108 changes: 59 additions & 49 deletions ext/opcache/jit/ir/ir.c
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ void ir_print_const(const ir_ctx *ctx, const ir_insn *insn, FILE *f, bool quoted
#define ir_op_flag_d0 ir_op_flag_d
#define ir_op_flag_d1 (ir_op_flag_d | 1 | (1 << IR_OP_FLAG_OPERANDS_SHIFT))
#define ir_op_flag_d1X1 (ir_op_flag_d | 1 | (2 << IR_OP_FLAG_OPERANDS_SHIFT))
#define ir_op_flag_d1X2 (ir_op_flag_d | 1 | (3 << IR_OP_FLAG_OPERANDS_SHIFT))
#define ir_op_flag_d2 (ir_op_flag_d | 2 | (2 << IR_OP_FLAG_OPERANDS_SHIFT))
#define ir_op_flag_d2C (ir_op_flag_d | IR_OP_FLAG_COMMUTATIVE | 2 | (2 << IR_OP_FLAG_OPERANDS_SHIFT))
#define ir_op_flag_d3 (ir_op_flag_d | 3 | (3 << IR_OP_FLAG_OPERANDS_SHIFT))
Expand Down Expand Up @@ -270,6 +271,7 @@ void ir_print_const(const ir_ctx *ctx, const ir_insn *insn, FILE *f, bool quoted
#define ir_op_flag_s3 (ir_op_flag_s | 3 | (3 << IR_OP_FLAG_OPERANDS_SHIFT))
#define ir_op_flag_x1 (IR_OP_FLAG_CONTROL|IR_OP_FLAG_MEM|IR_OP_FLAG_MEM_CALL | 1 | (1 << IR_OP_FLAG_OPERANDS_SHIFT))
#define ir_op_flag_x2 (IR_OP_FLAG_CONTROL|IR_OP_FLAG_MEM|IR_OP_FLAG_MEM_CALL | 2 | (2 << IR_OP_FLAG_OPERANDS_SHIFT))
#define ir_op_flag_x2X1 (IR_OP_FLAG_CONTROL|IR_OP_FLAG_MEM|IR_OP_FLAG_MEM_CALL | 2 | (3 << IR_OP_FLAG_OPERANDS_SHIFT))
#define ir_op_flag_x3 (IR_OP_FLAG_CONTROL|IR_OP_FLAG_MEM|IR_OP_FLAG_MEM_CALL | 3 | (3 << IR_OP_FLAG_OPERANDS_SHIFT))
#define ir_op_flag_xN (IR_OP_FLAG_CONTROL|IR_OP_FLAG_MEM|IR_OP_FLAG_MEM_CALL | IR_OP_FLAG_VAR_INPUTS)
#define ir_op_flag_a1 (IR_OP_FLAG_CONTROL|IR_OP_FLAG_MEM|IR_OP_FLAG_MEM_ALLOC | 1 | (1 << IR_OP_FLAG_OPERANDS_SHIFT))
Expand Down Expand Up @@ -392,6 +394,8 @@ void ir_init(ir_ctx *ctx, uint32_t flags, ir_ref consts_limit, ir_ref insns_limi
ctx->insns_limit = insns_limit;
ctx->consts_count = -(IR_TRUE - 1);
ctx->consts_limit = consts_limit;
ctx->const_hash = ctx->_const_hash;
ctx->const_hash_mask = IR_CONST_HASH_SIZE - 1;
ctx->fold_cse_limit = IR_UNUSED + 1;
ctx->flags = flags;

Expand All @@ -414,6 +418,9 @@ void ir_free(ir_ctx *ctx)
{
ir_insn *buf = ctx->ir_base - ctx->consts_limit;
ir_mem_free(buf);
if (ctx->value_params) {
ir_mem_free(ctx->value_params);
}
if (ctx->strtab.data) {
ir_strtab_free(&ctx->strtab);
}
Expand Down Expand Up @@ -468,6 +475,10 @@ void ir_free(ir_ctx *ctx)
ir_list_free((ir_list*)ctx->osr_entry_loads);
ir_mem_free(ctx->osr_entry_loads);
}

if (ctx->const_hash_mask != IR_CONST_HASH_SIZE - 1) {
ir_mem_free(ctx->const_hash);
}
}

ir_ref ir_unique_const_addr(ir_ctx *ctx, uintptr_t addr)
Expand All @@ -479,72 +490,64 @@ ir_ref ir_unique_const_addr(ir_ctx *ctx, uintptr_t addr)
insn->val.u64 = addr;
/* don't insert into constants chain */
insn->prev_const = IR_UNUSED;
#if 0
insn->prev_const = ctx->prev_const_chain[IR_ADDR];
ctx->prev_const_chain[IR_ADDR] = ref;
#endif
#if 0
ir_insn *prev_insn, *next_insn;
ir_ref next;

prev_insn = NULL;
next = ctx->prev_const_chain[IR_ADDR];
while (next) {
next_insn = &ctx->ir_base[next];
if (UNEXPECTED(next_insn->val.u64 >= addr)) {
break;
}
prev_insn = next_insn;
next = next_insn->prev_const;
}

if (prev_insn) {
insn->prev_const = prev_insn->prev_const;
prev_insn->prev_const = ref;
} else {
insn->prev_const = ctx->prev_const_chain[IR_ADDR];
ctx->prev_const_chain[IR_ADDR] = ref;
}
#endif

return ref;
}

IR_ALWAYS_INLINE uintptr_t ir_const_hash(ir_val val, uint32_t optx)
{
return (val.u64 ^ (val.u64 >> 32) ^ optx);
}

static IR_NEVER_INLINE void ir_const_hash_rehash(ir_ctx *ctx)
{
ir_insn *insn;
ir_ref ref;
uintptr_t hash;

if (ctx->const_hash_mask != IR_CONST_HASH_SIZE - 1) {
ir_mem_free(ctx->const_hash);
}
ctx->const_hash_mask = (ctx->const_hash_mask + 1) * 2 - 1;
ctx->const_hash = ir_mem_calloc(ctx->const_hash_mask + 1, sizeof(ir_ref));
for (ref = IR_TRUE - 1; ref > -ctx->consts_count; ref--) {
insn = &ctx->ir_base[ref];
hash = ir_const_hash(insn->val, insn->optx) & ctx->const_hash_mask;
insn->prev_const = ctx->const_hash[hash];
ctx->const_hash[hash] = ref;
}
}

ir_ref ir_const_ex(ir_ctx *ctx, ir_val val, uint8_t type, uint32_t optx)
{
ir_insn *insn, *prev_insn;
ir_insn *insn;
ir_ref ref, prev;
uintptr_t hash;

if (type == IR_BOOL) {
return val.u64 ? IR_TRUE : IR_FALSE;
} else if (type == IR_ADDR && val.u64 == 0) {
return IR_NULL;
}
prev_insn = NULL;
ref = ctx->prev_const_chain[type];

hash = ir_const_hash(val, optx) & ctx->const_hash_mask;
ref = ctx->const_hash[hash];
while (ref) {
insn = &ctx->ir_base[ref];
if (UNEXPECTED(insn->val.u64 >= val.u64)) {
if (insn->val.u64 == val.u64) {
if (insn->optx == optx) {
return ref;
}
} else {
break;
}
if (insn->val.u64 == val.u64 && insn->optx == optx) {
return ref;
}
prev_insn = insn;
ref = insn->prev_const;
}

if (prev_insn) {
prev = prev_insn->prev_const;
prev_insn->prev_const = -ctx->consts_count;
} else {
prev = ctx->prev_const_chain[type];
ctx->prev_const_chain[type] = -ctx->consts_count;
if ((uintptr_t)ctx->consts_count > ctx->const_hash_mask) {
ir_const_hash_rehash(ctx);
hash = ir_const_hash(val, optx) & ctx->const_hash_mask;
}

prev = ctx->const_hash[hash];
ctx->const_hash[hash] = -ctx->consts_count;

ref = ir_next_const(ctx);
insn = &ctx->ir_base[ref];
insn->prev_const = prev;
Expand Down Expand Up @@ -2092,10 +2095,10 @@ IR_ALWAYS_INLINE ir_ref ir_find_aliasing_vload_i(ir_ctx *ctx, ir_ref ref, ir_typ
if (insn->type == type) {
return ref; /* load forwarding (L2L) */
} else if (ir_type_size[insn->type] == ir_type_size[type]) {
return ir_fold1(ctx, IR_OPT(IR_BITCAST, type), ref); /* load forwarding with bitcast (L2L) */
return ref; /* load forwarding with bitcast (L2L) */
} else if (ir_type_size[insn->type] > ir_type_size[type]
&& IR_IS_TYPE_INT(type) && IR_IS_TYPE_INT(insn->type)) {
return ir_fold1(ctx, IR_OPT(IR_TRUNC, type), ref); /* partial load forwarding (L2L) */
return ref; /* partial load forwarding (L2L) */
}
}
} else if (insn->op == IR_VSTORE) {
Expand All @@ -2105,10 +2108,10 @@ IR_ALWAYS_INLINE ir_ref ir_find_aliasing_vload_i(ir_ctx *ctx, ir_ref ref, ir_typ
if (type2 == type) {
return insn->op3; /* store forwarding (S2L) */
} else if (ir_type_size[type2] == ir_type_size[type]) {
return ir_fold1(ctx, IR_OPT(IR_BITCAST, type), insn->op3); /* store forwarding with bitcast (S2L) */
return insn->op3; /* store forwarding with bitcast (S2L) */
} else if (ir_type_size[type2] > ir_type_size[type]
&& IR_IS_TYPE_INT(type) && IR_IS_TYPE_INT(type2)) {
return ir_fold1(ctx, IR_OPT(IR_TRUNC, type), insn->op3); /* partial store forwarding (S2L) */
return insn->op3; /* partial store forwarding (S2L) */
} else {
break;
}
Expand Down Expand Up @@ -3214,6 +3217,13 @@ ir_ref _ir_VA_ARG(ir_ctx *ctx, ir_type type, ir_ref list)
return ctx->control = ir_emit2(ctx, IR_OPT(IR_VA_ARG, type), ctx->control, list);
}

ir_ref _ir_VA_ARG_EX(ir_ctx *ctx, ir_type type, ir_ref list, size_t size)
{
IR_ASSERT(ctx->control);
IR_ASSERT(size <= 0x7fffffff);
return ctx->control = ir_emit3(ctx, IR_OPT(IR_VA_ARG, type), ctx->control, list, (ir_ref)size);
}

ir_ref _ir_BLOCK_BEGIN(ir_ctx *ctx)
{
IR_ASSERT(ctx->control);
Expand Down
19 changes: 17 additions & 2 deletions ext/opcache/jit/ir/ir.h
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,8 @@ typedef enum _ir_type {
_(PHI, pN, reg, def, def) /* SSA Phi function */ \
_(COPY, d1X1, def, opt, ___) /* COPY (last foldable op) */ \
_(PI, p2, reg, def, ___) /* e-SSA Pi constraint ??? */ \
_(ARGVAL, d1X2, def, num, num) /* pass struct arg by value */ \
/* (op2 - size, op3 - align) */ \
/* (USE, RENAME) */ \
\
/* data ops */ \
Expand Down Expand Up @@ -343,7 +345,8 @@ typedef enum _ir_type {
_(VA_START, x2, src, def, ___) /* va_start(va_list) */ \
_(VA_END, x2, src, def, ___) /* va_end(va_list) */ \
_(VA_COPY, x3, src, def, def) /* va_copy(dst, stc) */ \
_(VA_ARG, x2, src, def, ___) /* va_arg(va_list) */ \
_(VA_ARG, x2X1, src, def, opt) /* va_arg(va_list) */ \
/* op3 - (size<<3)+log2(align) */ \
\
/* guards */ \
_(GUARD, c3, src, def, def) /* IF without second successor */ \
Expand Down Expand Up @@ -583,19 +586,30 @@ typedef struct _ir_code_buffer {
void *pos;
} ir_code_buffer;

typedef struct {
int size;
int align;
int offset;
} ir_value_param;

#define IR_CONST_HASH_SIZE 64

struct _ir_ctx {
ir_insn *ir_base; /* two directional array - instructions grow down, constants grow up */
ir_ref insns_count; /* number of instructions stored in instructions buffer */
ir_ref insns_limit; /* size of allocated instructions buffer (it's extended when overflow) */
ir_ref consts_count; /* number of constants stored in constants buffer */
ir_ref consts_limit; /* size of allocated constants buffer (it's extended when overflow) */
uintptr_t const_hash_mask;
ir_ref *const_hash;
uint32_t flags; /* IR context flags (see IR_* defines above) */
uint32_t flags2; /* IR context private flags (see IR_* defines in ir_private.h) */
ir_type ret_type; /* Function return type */
uint32_t mflags; /* CPU specific flags (see IR_X86_... macros below) */
int32_t status; /* non-zero error code (see IR_ERROR_... macros), app may use negative codes */
ir_ref fold_cse_limit; /* CSE finds identical insns backward from "insn_count" to "fold_cse_limit" */
ir_insn fold_insn; /* temporary storage for folding engine */
ir_value_param *value_params; /* information about "by-val" struct parameters */
ir_hashtab *binding;
ir_use_list *use_lists; /* def->use lists for each instruction */
ir_ref *use_edges; /* the actual uses: use = ctx->use_edges[ctx->use_lists[def].refs + n] */
Expand Down Expand Up @@ -655,7 +669,7 @@ struct _ir_ctx {
ir_loader *loader;
ir_strtab strtab;
ir_ref prev_insn_chain[IR_LAST_FOLDABLE_OP + 1];
ir_ref prev_const_chain[IR_LAST_TYPE];
ir_ref _const_hash[IR_CONST_HASH_SIZE];
};

/* Basic IR Construction API (implementation in ir.c) */
Expand Down Expand Up @@ -896,6 +910,7 @@ int ir_load_llvm_asm(ir_loader *loader, const char *filename);
#define IR_SAVE_SAFE_NAMES (1<<5) /* add '@' prefix to symbol names */

void ir_print_proto(const ir_ctx *ctx, ir_ref proto, FILE *f);
void ir_print_proto_ex(uint8_t flags, ir_type ret_type, uint32_t params_count, const uint8_t *param_types, FILE *f);
void ir_save(const ir_ctx *ctx, uint32_t save_flags, FILE *f);

/* IR debug dump API (implementation in ir_dump.c) */
Expand Down
Loading
Loading