1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
|
/*
* bplist_behavior_test.c
*
* Tests binary plist parser behavior around the object data area:
*
* 1) Arrays and dictionaries are parsed correctly for different
* object reference sizes, including sizes that are not a power of 2.
*
* 2) Objects whose data extends beyond the object data area, i.e.
* into the offset table, must cause parsing to fail. This covers
* strings, extended size markers, array references and dictionary
* key/value references.
*
* The binary plists are assembled in memory with a minimal writer so
* each test case only has to specify the object bytes.
*/
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <inttypes.h>
#include "plist/plist.h"
#define MAX_BPLIST_SIZE 256
static void put_be64(uint8_t *p, uint64_t v)
{
int i;
for (i = 7; i >= 0; i--) {
p[i] = (uint8_t)(v & 0xFF);
v >>= 8;
}
}
/*
* Assemble a binary plist from the given object bytes. offsets[] holds the
* start of each object relative to the beginning of objs. A 1 byte offset
* table is placed directly after the objects, followed by the trailer.
*/
static uint32_t build_bplist(uint8_t *out, const uint8_t *objs, size_t objs_len, const uint8_t *offsets, size_t num_objects, uint8_t ref_size, uint64_t root)
{
size_t len = 0;
size_t i;
memcpy(out, "bplist00", 8);
len = 8;
memcpy(out + len, objs, objs_len);
len += objs_len;
size_t offset_table_offset = len;
for (i = 0; i < num_objects; i++) {
out[len++] = (uint8_t)(8 + offsets[i]);
}
memset(out + len, 0, 6);
out[len + 6] = 1; /* offset size */
out[len + 7] = ref_size;
put_be64(out + len + 8, num_objects);
put_be64(out + len + 16, root);
put_be64(out + len + 24, offset_table_offset);
len += 32;
return (uint32_t)len;
}
static int check_int_item(plist_t item, uint64_t expected, const char *name)
{
uint64_t val = 0;
if (plist_get_node_type(item) != PLIST_INT) {
fprintf(stderr, "%s: expected PLIST_INT item\n", name);
return 0;
}
plist_get_uint_val(item, &val);
if (val != expected) {
fprintf(stderr, "%s: expected %" PRIu64 ", got %" PRIu64 "\n", name, expected, val);
return 0;
}
return 1;
}
static int test_array_ref_sizes(void)
{
/* [ 1, 2 ] with 1 byte object references */
const uint8_t objs1[] = { 0xA2, 0x01, 0x02, 0x10, 0x01, 0x10, 0x02 };
const uint8_t offs1[] = { 0, 3, 5 };
/* [ 1, 2 ] with 3 byte object references */
const uint8_t objs3[] = { 0xA2, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x10, 0x01, 0x10, 0x02 };
const uint8_t offs3[] = { 0, 7, 9 };
struct {
const uint8_t *objs;
size_t objs_len;
const uint8_t *offs;
uint8_t ref_size;
} cases[] = {
{ objs1, sizeof(objs1), offs1, 1 },
{ objs3, sizeof(objs3), offs3, 3 },
};
size_t i;
for (i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
uint8_t buf[MAX_BPLIST_SIZE];
uint32_t len = build_bplist(buf, cases[i].objs, cases[i].objs_len, cases[i].offs, 3, cases[i].ref_size, 0);
plist_t root = NULL;
plist_err_t err = plist_from_bin((const char*)buf, len, &root);
if (err != PLIST_ERR_SUCCESS || !root) {
fprintf(stderr, "array ref_size %u: plist_from_bin failed (err=%d)\n", cases[i].ref_size, err);
plist_free(root);
return 0;
}
if (plist_get_node_type(root) != PLIST_ARRAY || plist_array_get_size(root) != 2) {
fprintf(stderr, "array ref_size %u: expected array with 2 items\n", cases[i].ref_size);
plist_free(root);
return 0;
}
if (!check_int_item(plist_array_get_item(root, 0), 1, "array item 0") ||
!check_int_item(plist_array_get_item(root, 1), 2, "array item 1")) {
plist_free(root);
return 0;
}
plist_free(root);
}
return 1;
}
static int test_dict_ref_size(void)
{
/* { "a": 5 } with 2 byte object references */
const uint8_t objs[] = { 0xD1, 0x00, 0x01, 0x00, 0x02, 0x51, 'a', 0x10, 0x05 };
const uint8_t offs[] = { 0, 5, 7 };
uint8_t buf[MAX_BPLIST_SIZE];
uint32_t len = build_bplist(buf, objs, sizeof(objs), offs, 3, 2, 0);
plist_t root = NULL;
plist_err_t err = plist_from_bin((const char*)buf, len, &root);
if (err != PLIST_ERR_SUCCESS || !root) {
fprintf(stderr, "dict ref_size 2: plist_from_bin failed (err=%d)\n", err);
plist_free(root);
return 0;
}
if (plist_get_node_type(root) != PLIST_DICT || plist_dict_get_size(root) != 1) {
fprintf(stderr, "dict ref_size 2: expected dict with 1 entry\n");
plist_free(root);
return 0;
}
if (!check_int_item(plist_dict_get_item(root, "a"), 5, "dict value")) {
plist_free(root);
return 0;
}
plist_free(root);
return 1;
}
static int expect_rejected(const uint8_t *objs, size_t objs_len, const uint8_t *offs, size_t num_objects, uint64_t root_index, const char *name)
{
uint8_t buf[MAX_BPLIST_SIZE];
uint32_t len = build_bplist(buf, objs, objs_len, offs, num_objects, 1, root_index);
plist_t root = NULL;
plist_err_t err = plist_from_bin((const char*)buf, len, &root);
if (err == PLIST_ERR_SUCCESS || root != NULL) {
fprintf(stderr, "%s: expected failure, got err=%d root=%p\n", name, err, (void*)root);
plist_free(root);
return 0;
}
return 1;
}
static int test_data_beyond_object_area_is_rejected(void)
{
int ok = 1;
/* string claims 5 bytes, only 2 are present before the offset table */
const uint8_t str_objs[] = { 0x55, 'a', 'b' };
const uint8_t str_offs[] = { 0 };
ok &= expect_rejected(str_objs, sizeof(str_objs), str_offs, 1, 0, "truncated string");
/* extended size marker, but no size integer before the offset table */
const uint8_t ext_objs[] = { 0x5F };
const uint8_t ext_offs[] = { 0 };
ok &= expect_rejected(ext_objs, sizeof(ext_objs), ext_offs, 1, 0, "missing size integer");
/* array claims 3 references, only 2 are present before the offset table */
const uint8_t arr_objs[] = { 0x10, 0x01, 0x10, 0x02, 0xA3, 0x00, 0x01 };
const uint8_t arr_offs[] = { 0, 2, 4 };
ok &= expect_rejected(arr_objs, sizeof(arr_objs), arr_offs, 3, 2, "truncated array references");
/* dict has its key reference, but the value reference is missing */
const uint8_t dict_objs[] = { 0x51, 'a', 0x10, 0x05, 0xD1, 0x00 };
const uint8_t dict_offs[] = { 0, 2, 4 };
ok &= expect_rejected(dict_objs, sizeof(dict_objs), dict_offs, 3, 2, "truncated dict references");
return ok;
}
int main(void)
{
int ok = 1;
ok &= test_array_ref_sizes();
ok &= test_dict_ref_size();
ok &= test_data_beyond_object_area_is_rejected();
return ok ? 0 : 1;
}
|