Skip to content

Commit e7d7259

Browse files
committed
Fix out of bounds write in array deserialization
The array was allocated based on the serialized `elsize` of the array, however, unions get an extra selector array after the regular storage which was not allocated (because we didn't know it was gonna be a union array at the time when we allocated it). According to a48eeef we cannot look at the element type to allocate the array, so we need to serialize a bit to indicate that we will have a union array. Fixes #28998
1 parent c8450d8 commit e7d7259

File tree

4 files changed

+30
-15
lines changed

4 files changed

+30
-15
lines changed

src/array.c

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ size_t jl_arr_xtralloc_limit = 0;
5353
#define MAXINTVAL (((size_t)-1)>>1)
5454

5555
static jl_array_t *_new_array_(jl_value_t *atype, uint32_t ndims, size_t *dims,
56-
int isunboxed, int elsz)
56+
int isunboxed, int isunion, int elsz)
5757
{
5858
jl_ptls_t ptls = jl_get_ptls_states();
5959
size_t i, tot, nel=1;
@@ -67,7 +67,7 @@ static jl_array_t *_new_array_(jl_value_t *atype, uint32_t ndims, size_t *dims,
6767
jl_error("invalid Array dimensions");
6868
nel = prod;
6969
}
70-
int isunion = atype != NULL && jl_is_uniontype(jl_tparam0(atype));
70+
assert(atype == NULL || isunion == jl_is_uniontype(jl_tparam0(atype)));
7171
if (isunboxed) {
7272
wideint_t prod = (wideint_t)elsz * (wideint_t)nel;
7373
if (prod > (wideint_t) MAXINTVAL)
@@ -149,18 +149,19 @@ static inline jl_array_t *_new_array(jl_value_t *atype, uint32_t ndims, size_t *
149149
jl_value_t *eltype = jl_tparam0(atype);
150150
size_t elsz = 0, al = 0;
151151
int isunboxed = jl_islayout_inline(eltype, &elsz, &al);
152+
int isunion = jl_is_uniontype(eltype);
152153
if (!isunboxed) {
153154
elsz = sizeof(void*);
154155
al = elsz;
155156
}
156157

157-
return _new_array_(atype, ndims, dims, isunboxed, elsz);
158+
return _new_array_(atype, ndims, dims, isunboxed, isunion, elsz);
158159
}
159160

160161
jl_array_t *jl_new_array_for_deserialization(jl_value_t *atype, uint32_t ndims, size_t *dims,
161-
int isunboxed, int elsz)
162+
int isunboxed, int isunion, int elsz)
162163
{
163-
return _new_array_(atype, ndims, dims, isunboxed, elsz);
164+
return _new_array_(atype, ndims, dims, isunboxed, isunion, elsz);
164165
}
165166

166167
#ifndef JL_NDEBUG
@@ -1115,8 +1116,10 @@ JL_DLLEXPORT jl_array_t *jl_array_copy(jl_array_t *ary)
11151116
{
11161117
size_t elsz = ary->elsize;
11171118
size_t len = jl_array_len(ary);
1119+
int isunion = jl_is_uniontype(jl_tparam0(jl_typeof(ary)));
11181120
jl_array_t *new_ary = _new_array_(jl_typeof(ary), jl_array_ndims(ary),
1119-
&ary->nrows, !ary->flags.ptrarray, elsz);
1121+
&ary->nrows, !ary->flags.ptrarray,
1122+
isunion, elsz);
11201123
memcpy(new_ary->data, ary->data, len * elsz);
11211124
// ensure isbits union arrays copy their selector bytes correctly
11221125
if (jl_array_isbitsunion(ary))

src/dump.c

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -640,14 +640,15 @@ static void jl_serialize_value_(jl_serializer_state *s, jl_value_t *v, int as_li
640640
}
641641
else if (jl_is_array(v)) {
642642
jl_array_t *ar = (jl_array_t*)v;
643-
if (ar->flags.ndims == 1 && ar->elsize < 128) {
643+
int isunion = jl_is_uniontype(jl_tparam0(jl_typeof(ar)));
644+
if (ar->flags.ndims == 1 && ar->elsize <= 0x3f) {
644645
write_uint8(s->s, TAG_ARRAY1D);
645-
write_uint8(s->s, (ar->flags.ptrarray<<7) | (ar->elsize & 0x7f));
646+
write_uint8(s->s, (ar->flags.ptrarray<<7) | (isunion << 6) | (ar->elsize & 0x3f));
646647
}
647648
else {
648649
write_uint8(s->s, TAG_ARRAY);
649650
write_uint16(s->s, ar->flags.ndims);
650-
write_uint16(s->s, (ar->flags.ptrarray<<15) | (ar->elsize & 0x7fff));
651+
write_uint16(s->s, (ar->flags.ptrarray << 15) | (isunion << 14) | (ar->elsize & 0x3fff));
651652
}
652653
for (i = 0; i < ar->flags.ndims; i++)
653654
jl_serialize_value(s, jl_box_long(jl_array_dim(ar,i)));
@@ -1211,7 +1212,7 @@ static void write_mod_list(ios_t *s, jl_array_t *a)
12111212
}
12121213

12131214
// "magic" string and version header of .ji file
1214-
static const int JI_FORMAT_VERSION = 6;
1215+
static const int JI_FORMAT_VERSION = 7;
12151216
static const char JI_MAGIC[] = "\373jli\r\n\032\n"; // based on PNG signature
12161217
static const uint16_t BOM = 0xFEFF; // byte-order marker
12171218
static void write_header(ios_t *s)
@@ -1496,18 +1497,20 @@ static jl_value_t *jl_deserialize_value_array(jl_serializer_state *s, uint8_t ta
14961497
{
14971498
int usetable = (s->mode != MODE_IR);
14981499
int16_t i, ndims;
1499-
int isunboxed, elsize;
1500+
int isunboxed, isunion, elsize;
15001501
if (tag == TAG_ARRAY1D) {
15011502
ndims = 1;
15021503
elsize = read_uint8(s->s);
15031504
isunboxed = !(elsize >> 7);
1504-
elsize = elsize & 0x7f;
1505+
isunion = elsize >> 6;
1506+
elsize = elsize & 0x3f;
15051507
}
15061508
else {
15071509
ndims = read_uint16(s->s);
15081510
elsize = read_uint16(s->s);
15091511
isunboxed = !(elsize >> 15);
1510-
elsize = elsize & 0x7fff;
1512+
isunion = elsize >> 14;
1513+
elsize = elsize & 0x3fff;
15111514
}
15121515
uintptr_t pos = backref_list.len;
15131516
if (usetable)
@@ -1516,7 +1519,8 @@ static jl_value_t *jl_deserialize_value_array(jl_serializer_state *s, uint8_t ta
15161519
for (i = 0; i < ndims; i++) {
15171520
dims[i] = jl_unbox_long(jl_deserialize_value(s, NULL));
15181521
}
1519-
jl_array_t *a = jl_new_array_for_deserialization((jl_value_t*)NULL, ndims, dims, isunboxed, elsize);
1522+
jl_array_t *a = jl_new_array_for_deserialization(
1523+
(jl_value_t*)NULL, ndims, dims, isunboxed, isunion, elsize);
15201524
if (usetable)
15211525
backref_list.items[pos] = a;
15221526
jl_value_t *aty = jl_deserialize_value(s, &jl_astaggedvalue(a)->type);

src/julia_internal.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,7 @@ JL_DLLEXPORT jl_value_t *jl_argument_datatype(jl_value_t *argt JL_PROPAGATES_ROO
463463
jl_value_t *jl_nth_slot_type(jl_value_t *sig JL_PROPAGATES_ROOT, size_t i) JL_NOTSAFEPOINT;
464464
void jl_compute_field_offsets(jl_datatype_t *st);
465465
jl_array_t *jl_new_array_for_deserialization(jl_value_t *atype, uint32_t ndims, size_t *dims,
466-
int isunboxed, int elsz);
466+
int isunboxed, int isunion, int elsz);
467467
void jl_module_run_initializer(jl_module_t *m);
468468
jl_binding_t *jl_get_module_binding(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var) JL_NOTSAFEPOINT;
469469
extern jl_array_t *jl_module_init_order JL_GLOBALLY_ROOTED;

test/precompile.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,12 @@ try
136136
const x28297 = Result(missing)
137137
138138
139+
# issue #28998
140+
const x28998 = [missing, 2, missing, 6, missing,
141+
missing, missing, missing,
142+
missing, missing, missing,
143+
missing, missing, 6]
144+
139145
let some_method = which(Base.include, (String,))
140146
# global const some_method // FIXME: support for serializing a direct reference to an external Method not implemented
141147
global const some_linfo =
@@ -180,6 +186,8 @@ try
180186
@test Foo.abigint_x::BigInt + 1 == big"125"
181187

182188
@test Foo.x28297.result === missing
189+
190+
@test Foo.x28998[end] == 6
183191
end
184192

185193
cachedir = joinpath(dir, "compiled", "v$(VERSION.major).$(VERSION.minor)")

0 commit comments

Comments
 (0)