diff options
author | Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> | 2012-07-02 20:03:30 +1000 |
---|---|---|
committer | Kristian Høgsberg <krh@bitplanet.net> | 2012-07-02 13:53:02 -0400 |
commit | 161c690b558fff9d0f407311ae4e1fbe389775f7 (patch) | |
tree | d09812a18f9c6e198732d10034040bf641ac4c85 | |
parent | d2bcffc470cf37dbdeb4d1ed46819360c16d92eb (diff) |
protocol: Add explicit nullable types
Most of the time it does not make sense to pass a NULL object, string, or array
to a protocol request. This commit adds an explicit “allow-null” attribute
to mark the request arguments where NULL makes sense.
Passing a NULL object, string, or array to a protocol request which is not
marked as allow-null is now an error. An implementation will never receive
a NULL value for these arguments from a client.
Signed-off-by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
-rw-r--r-- | src/connection.c | 81 | ||||
-rw-r--r-- | src/scanner.c | 32 | ||||
-rw-r--r-- | tests/connection-test.c | 87 |
3 files changed, 188 insertions, 12 deletions
diff --git a/src/connection.c b/src/connection.c index 72ca908..5946556 100644 --- a/src/connection.c +++ b/src/connection.c | |||
@@ -404,6 +404,36 @@ wl_connection_put_fd(struct wl_connection *connection, int32_t fd) | |||
404 | return 0; | 404 | return 0; |
405 | } | 405 | } |
406 | 406 | ||
407 | struct argument_details { | ||
408 | char type; | ||
409 | int nullable; | ||
410 | }; | ||
411 | |||
412 | static const char * | ||
413 | get_next_argument(const char *signature, struct argument_details *details) | ||
414 | { | ||
415 | if (*signature == '?') { | ||
416 | details->nullable = 1; | ||
417 | signature++; | ||
418 | } else | ||
419 | details->nullable = 0; | ||
420 | |||
421 | details->type = *signature; | ||
422 | return signature + 1; | ||
423 | } | ||
424 | |||
425 | static int | ||
426 | arg_count_for_signature(const char *signature) | ||
427 | { | ||
428 | int count = 0; | ||
429 | while (*signature) { | ||
430 | if (*signature != '?') | ||
431 | count++; | ||
432 | signature++; | ||
433 | } | ||
434 | return count; | ||
435 | } | ||
436 | |||
407 | struct wl_closure * | 437 | struct wl_closure * |
408 | wl_closure_vmarshal(struct wl_object *sender, | 438 | wl_closure_vmarshal(struct wl_object *sender, |
409 | uint32_t opcode, va_list ap, | 439 | uint32_t opcode, va_list ap, |
@@ -415,6 +445,8 @@ wl_closure_vmarshal(struct wl_object *sender, | |||
415 | int dup_fd; | 445 | int dup_fd; |
416 | struct wl_array **arrayp, *array; | 446 | struct wl_array **arrayp, *array; |
417 | const char **sp, *s; | 447 | const char **sp, *s; |
448 | const char *signature = message->signature; | ||
449 | struct argument_details arg; | ||
418 | char *extra; | 450 | char *extra; |
419 | int i, count, fd, extra_size, *fd_ptr; | 451 | int i, count, fd, extra_size, *fd_ptr; |
420 | 452 | ||
@@ -424,7 +456,7 @@ wl_closure_vmarshal(struct wl_object *sender, | |||
424 | return NULL; | 456 | return NULL; |
425 | 457 | ||
426 | extra_size = wl_message_size_extra(message); | 458 | extra_size = wl_message_size_extra(message); |
427 | count = strlen(message->signature) + 2; | 459 | count = arg_count_for_signature(signature) + 2; |
428 | extra = (char *) closure->buffer; | 460 | extra = (char *) closure->buffer; |
429 | start = &closure->buffer[DIV_ROUNDUP(extra_size, sizeof *p)]; | 461 | start = &closure->buffer[DIV_ROUNDUP(extra_size, sizeof *p)]; |
430 | end = &closure->buffer[256]; | 462 | end = &closure->buffer[256]; |
@@ -434,7 +466,9 @@ wl_closure_vmarshal(struct wl_object *sender, | |||
434 | closure->types[1] = &ffi_type_pointer; | 466 | closure->types[1] = &ffi_type_pointer; |
435 | 467 | ||
436 | for (i = 2; i < count; i++) { | 468 | for (i = 2; i < count; i++) { |
437 | switch (message->signature[i - 2]) { | 469 | signature = get_next_argument(signature, &arg); |
470 | |||
471 | switch (arg.type) { | ||
438 | case 'f': | 472 | case 'f': |
439 | closure->types[i] = &ffi_type_sint32; | 473 | closure->types[i] = &ffi_type_sint32; |
440 | closure->args[i] = p; | 474 | closure->args[i] = p; |
@@ -463,6 +497,10 @@ wl_closure_vmarshal(struct wl_object *sender, | |||
463 | extra += sizeof *sp; | 497 | extra += sizeof *sp; |
464 | 498 | ||
465 | s = va_arg(ap, const char *); | 499 | s = va_arg(ap, const char *); |
500 | |||
501 | if (!arg.nullable && s == NULL) | ||
502 | goto err_null; | ||
503 | |||
466 | length = s ? strlen(s) + 1: 0; | 504 | length = s ? strlen(s) + 1: 0; |
467 | if (p + DIV_ROUNDUP(length, sizeof *p) + 1 > end) | 505 | if (p + DIV_ROUNDUP(length, sizeof *p) + 1 > end) |
468 | goto err; | 506 | goto err; |
@@ -483,6 +521,10 @@ wl_closure_vmarshal(struct wl_object *sender, | |||
483 | extra += sizeof *objectp; | 521 | extra += sizeof *objectp; |
484 | 522 | ||
485 | object = va_arg(ap, struct wl_object *); | 523 | object = va_arg(ap, struct wl_object *); |
524 | |||
525 | if (!arg.nullable && object == NULL) | ||
526 | goto err_null; | ||
527 | |||
486 | *objectp = object; | 528 | *objectp = object; |
487 | if (end - p < 1) | 529 | if (end - p < 1) |
488 | goto err; | 530 | goto err; |
@@ -508,6 +550,10 @@ wl_closure_vmarshal(struct wl_object *sender, | |||
508 | extra += sizeof **arrayp; | 550 | extra += sizeof **arrayp; |
509 | 551 | ||
510 | array = va_arg(ap, struct wl_array *); | 552 | array = va_arg(ap, struct wl_array *); |
553 | |||
554 | if (!arg.nullable && array == NULL) | ||
555 | goto err_null; | ||
556 | |||
511 | if (array == NULL || array->size == 0) { | 557 | if (array == NULL || array->size == 0) { |
512 | if (end - p < 1) | 558 | if (end - p < 1) |
513 | goto err; | 559 | goto err; |
@@ -542,7 +588,7 @@ wl_closure_vmarshal(struct wl_object *sender, | |||
542 | break; | 588 | break; |
543 | default: | 589 | default: |
544 | fprintf(stderr, "unhandled format code: '%c'\n", | 590 | fprintf(stderr, "unhandled format code: '%c'\n", |
545 | message->signature[i - 2]); | 591 | arg.type); |
546 | assert(0); | 592 | assert(0); |
547 | break; | 593 | break; |
548 | } | 594 | } |
@@ -567,6 +613,15 @@ err: | |||
567 | errno = ENOMEM; | 613 | errno = ENOMEM; |
568 | 614 | ||
569 | return NULL; | 615 | return NULL; |
616 | |||
617 | err_null: | ||
618 | free(closure); | ||
619 | wl_log("error marshalling arguments for %s:%i.%s (signature %s): " | ||
620 | "null value passed for arg %i\n", | ||
621 | sender->interface->name, sender->id, message->name, | ||
622 | message->signature, i); | ||
623 | errno = EINVAL; | ||
624 | return NULL; | ||
570 | } | 625 | } |
571 | 626 | ||
572 | struct wl_closure * | 627 | struct wl_closure * |
@@ -579,11 +634,13 @@ wl_connection_demarshal(struct wl_connection *connection, | |||
579 | int *fd; | 634 | int *fd; |
580 | char *extra, **s; | 635 | char *extra, **s; |
581 | unsigned int i, count, extra_space; | 636 | unsigned int i, count, extra_space; |
637 | const char *signature = message->signature; | ||
638 | struct argument_details arg; | ||
582 | struct wl_object **object; | 639 | struct wl_object **object; |
583 | struct wl_array **array; | 640 | struct wl_array **array; |
584 | struct wl_closure *closure; | 641 | struct wl_closure *closure; |
585 | 642 | ||
586 | count = strlen(message->signature) + 2; | 643 | count = arg_count_for_signature(signature) + 2; |
587 | if (count > ARRAY_LENGTH(closure->types)) { | 644 | if (count > ARRAY_LENGTH(closure->types)) { |
588 | printf("too many args (%d)\n", count); | 645 | printf("too many args (%d)\n", count); |
589 | errno = EINVAL; | 646 | errno = EINVAL; |
@@ -606,6 +663,8 @@ wl_connection_demarshal(struct wl_connection *connection, | |||
606 | end = (uint32_t *) ((char *) p + size); | 663 | end = (uint32_t *) ((char *) p + size); |
607 | extra = (char *) end; | 664 | extra = (char *) end; |
608 | for (i = 2; i < count; i++) { | 665 | for (i = 2; i < count; i++) { |
666 | signature = get_next_argument(signature, &arg); | ||
667 | |||
609 | if (p + 1 > end) { | 668 | if (p + 1 > end) { |
610 | printf("message too short, " | 669 | printf("message too short, " |
611 | "object (%d), message %s(%s)\n", | 670 | "object (%d), message %s(%s)\n", |
@@ -614,7 +673,7 @@ wl_connection_demarshal(struct wl_connection *connection, | |||
614 | goto err; | 673 | goto err; |
615 | } | 674 | } |
616 | 675 | ||
617 | switch (message->signature[i - 2]) { | 676 | switch (arg.type) { |
618 | case 'u': | 677 | case 'u': |
619 | closure->types[i] = &ffi_type_uint32; | 678 | closure->types[i] = &ffi_type_uint32; |
620 | closure->args[i] = p++; | 679 | closure->args[i] = p++; |
@@ -784,11 +843,14 @@ copy_fds_to_connection(struct wl_closure *closure, | |||
784 | { | 843 | { |
785 | const struct wl_message *message = closure->message; | 844 | const struct wl_message *message = closure->message; |
786 | uint32_t i, count; | 845 | uint32_t i, count; |
846 | struct argument_details arg; | ||
847 | const char *signature = message->signature; | ||
787 | int *fd; | 848 | int *fd; |
788 | 849 | ||
789 | count = strlen(message->signature) + 2; | 850 | count = arg_count_for_signature(signature) + 2; |
790 | for (i = 2; i < count; i++) { | 851 | for (i = 2; i < count; i++) { |
791 | if (message->signature[i - 2] != 'h') | 852 | signature = get_next_argument(signature, &arg); |
853 | if (arg.type != 'h') | ||
792 | continue; | 854 | continue; |
793 | 855 | ||
794 | fd = closure->args[i]; | 856 | fd = closure->args[i]; |
@@ -834,6 +896,8 @@ wl_closure_print(struct wl_closure *closure, struct wl_object *target, int send) | |||
834 | union wl_value *value; | 896 | union wl_value *value; |
835 | int32_t si; | 897 | int32_t si; |
836 | int i; | 898 | int i; |
899 | struct argument_details arg; | ||
900 | const char *signature = closure->message->signature; | ||
837 | struct timespec tp; | 901 | struct timespec tp; |
838 | unsigned int time; | 902 | unsigned int time; |
839 | 903 | ||
@@ -847,11 +911,12 @@ wl_closure_print(struct wl_closure *closure, struct wl_object *target, int send) | |||
847 | closure->message->name); | 911 | closure->message->name); |
848 | 912 | ||
849 | for (i = 2; i < closure->count; i++) { | 913 | for (i = 2; i < closure->count; i++) { |
914 | signature = get_next_argument(signature, &arg); | ||
850 | if (i > 2) | 915 | if (i > 2) |
851 | fprintf(stderr, ", "); | 916 | fprintf(stderr, ", "); |
852 | 917 | ||
853 | value = closure->args[i]; | 918 | value = closure->args[i]; |
854 | switch (closure->message->signature[i - 2]) { | 919 | switch (arg.type) { |
855 | case 'u': | 920 | case 'u': |
856 | fprintf(stderr, "%u", value->uint32); | 921 | fprintf(stderr, "%u", value->uint32); |
857 | break; | 922 | break; |
diff --git a/src/scanner.c b/src/scanner.c index be8d3d6..4d4537c 100644 --- a/src/scanner.c +++ b/src/scanner.c | |||
@@ -93,6 +93,7 @@ enum arg_type { | |||
93 | struct arg { | 93 | struct arg { |
94 | char *name; | 94 | char *name; |
95 | enum arg_type type; | 95 | enum arg_type type; |
96 | int nullable; | ||
96 | char *interface_name; | 97 | char *interface_name; |
97 | struct wl_list link; | 98 | struct wl_list link; |
98 | char *summary; | 99 | char *summary; |
@@ -220,6 +221,20 @@ fail(struct parse_context *ctx, const char *msg) | |||
220 | exit(EXIT_FAILURE); | 221 | exit(EXIT_FAILURE); |
221 | } | 222 | } |
222 | 223 | ||
224 | static int | ||
225 | is_nullable_type(struct arg *arg) | ||
226 | { | ||
227 | switch (arg->type) { | ||
228 | /* Strings, objects, and arrays are possibly nullable */ | ||
229 | case STRING: | ||
230 | case OBJECT: | ||
231 | case ARRAY: | ||
232 | return 1; | ||
233 | default: | ||
234 | return 0; | ||
235 | } | ||
236 | } | ||
237 | |||
223 | static void | 238 | static void |
224 | start_element(void *data, const char *element_name, const char **atts) | 239 | start_element(void *data, const char *element_name, const char **atts) |
225 | { | 240 | { |
@@ -231,6 +246,7 @@ start_element(void *data, const char *element_name, const char **atts) | |||
231 | struct entry *entry; | 246 | struct entry *entry; |
232 | struct description *description; | 247 | struct description *description; |
233 | const char *name, *type, *interface_name, *value, *summary, *since; | 248 | const char *name, *type, *interface_name, *value, *summary, *since; |
249 | const char *allow_null; | ||
234 | char *end; | 250 | char *end; |
235 | int i, version; | 251 | int i, version; |
236 | 252 | ||
@@ -242,6 +258,7 @@ start_element(void *data, const char *element_name, const char **atts) | |||
242 | summary = NULL; | 258 | summary = NULL; |
243 | description = NULL; | 259 | description = NULL; |
244 | since = NULL; | 260 | since = NULL; |
261 | allow_null = NULL; | ||
245 | for (i = 0; atts[i]; i += 2) { | 262 | for (i = 0; atts[i]; i += 2) { |
246 | if (strcmp(atts[i], "name") == 0) | 263 | if (strcmp(atts[i], "name") == 0) |
247 | name = atts[i + 1]; | 264 | name = atts[i + 1]; |
@@ -257,6 +274,8 @@ start_element(void *data, const char *element_name, const char **atts) | |||
257 | summary = atts[i + 1]; | 274 | summary = atts[i + 1]; |
258 | if (strcmp(atts[i], "since") == 0) | 275 | if (strcmp(atts[i], "since") == 0) |
259 | since = atts[i + 1]; | 276 | since = atts[i + 1]; |
277 | if (strcmp(atts[i], "allow-null") == 0) | ||
278 | allow_null = atts[i + 1]; | ||
260 | } | 279 | } |
261 | 280 | ||
262 | ctx->character_data_length = 0; | 281 | ctx->character_data_length = 0; |
@@ -364,6 +383,16 @@ start_element(void *data, const char *element_name, const char **atts) | |||
364 | break; | 383 | break; |
365 | } | 384 | } |
366 | 385 | ||
386 | if (allow_null == NULL || strcmp(allow_null, "false") == 0) | ||
387 | arg->nullable = 0; | ||
388 | else if (strcmp(allow_null, "true") == 0) | ||
389 | arg->nullable = 1; | ||
390 | else | ||
391 | fail(ctx, "invalid value for allow-null attribute"); | ||
392 | |||
393 | if (allow_null != NULL && !is_nullable_type(arg)) | ||
394 | fail(ctx, "allow-null is only valid for objects, strings, and arrays"); | ||
395 | |||
367 | arg->summary = NULL; | 396 | arg->summary = NULL; |
368 | if (summary) | 397 | if (summary) |
369 | arg->summary = strdup(summary); | 398 | arg->summary = strdup(summary); |
@@ -969,6 +998,9 @@ emit_messages(struct wl_list *message_list, | |||
969 | wl_list_for_each(m, message_list, link) { | 998 | wl_list_for_each(m, message_list, link) { |
970 | printf("\t{ \"%s\", \"", m->name); | 999 | printf("\t{ \"%s\", \"", m->name); |
971 | wl_list_for_each(a, &m->arg_list, link) { | 1000 | wl_list_for_each(a, &m->arg_list, link) { |
1001 | if (is_nullable_type(a) && a->nullable) | ||
1002 | printf("?"); | ||
1003 | |||
972 | switch (a->type) { | 1004 | switch (a->type) { |
973 | default: | 1005 | default: |
974 | case INT: | 1006 | case INT: |
diff --git a/tests/connection-test.c b/tests/connection-test.c index 066b4bc..a852c17 100644 --- a/tests/connection-test.c +++ b/tests/connection-test.c | |||
@@ -233,9 +233,6 @@ TEST(connection_marshal) | |||
233 | marshal(&data, "o", 12, &object); | 233 | marshal(&data, "o", 12, &object); |
234 | assert(data.buffer[2] == object.id); | 234 | assert(data.buffer[2] == object.id); |
235 | 235 | ||
236 | marshal(&data, "o", 12, NULL); | ||
237 | assert(data.buffer[2] == 0); | ||
238 | |||
239 | marshal(&data, "n", 12, &object); | 236 | marshal(&data, "n", 12, &object); |
240 | assert(data.buffer[2] == object.id); | 237 | assert(data.buffer[2] == object.id); |
241 | 238 | ||
@@ -252,6 +249,68 @@ TEST(connection_marshal) | |||
252 | } | 249 | } |
253 | 250 | ||
254 | static void | 251 | static void |
252 | expected_fail_marshal(int expected_error, const char *format, ...) | ||
253 | { | ||
254 | struct wl_closure *closure; | ||
255 | static const uint32_t opcode = 4444; | ||
256 | static const struct wl_interface test_interface = { | ||
257 | .name = "test_object" | ||
258 | }; | ||
259 | static struct wl_object sender = { 0 }; | ||
260 | struct wl_message message = { "test", format, NULL }; | ||
261 | |||
262 | sender.interface = &test_interface; | ||
263 | sender.id = 1234; | ||
264 | va_list ap; | ||
265 | |||
266 | va_start(ap, format); | ||
267 | closure = wl_closure_vmarshal(&sender, opcode, ap, &message); | ||
268 | va_end(ap); | ||
269 | |||
270 | assert(closure == NULL); | ||
271 | assert(errno == expected_error); | ||
272 | } | ||
273 | |||
274 | TEST(connection_marshal_nullables) | ||
275 | { | ||
276 | struct marshal_data data; | ||
277 | struct wl_object object; | ||
278 | struct wl_array array; | ||
279 | const char text[] = "curry"; | ||
280 | |||
281 | setup_marshal_data(&data); | ||
282 | |||
283 | expected_fail_marshal(EINVAL, "o", NULL); | ||
284 | expected_fail_marshal(EINVAL, "s", NULL); | ||
285 | expected_fail_marshal(EINVAL, "a", NULL); | ||
286 | |||
287 | marshal(&data, "?o", 12, NULL); | ||
288 | assert(data.buffer[2] == 0); | ||
289 | |||
290 | marshal(&data, "?a", 12, NULL); | ||
291 | assert(data.buffer[2] == 0); | ||
292 | |||
293 | marshal(&data, "?s", 12, NULL); | ||
294 | assert(data.buffer[2] == 0); | ||
295 | |||
296 | object.id = 55293; | ||
297 | marshal(&data, "?o", 12, &object); | ||
298 | assert(data.buffer[2] == object.id); | ||
299 | |||
300 | array.data = (void *) text; | ||
301 | array.size = sizeof text; | ||
302 | marshal(&data, "?a", 20, &array); | ||
303 | assert(data.buffer[2] == array.size); | ||
304 | assert(memcmp(&data.buffer[3], text, array.size) == 0); | ||
305 | |||
306 | marshal(&data, "?s", 20, text); | ||
307 | assert(data.buffer[2] == sizeof text); | ||
308 | assert(strcmp((char *) &data.buffer[3], text) == 0); | ||
309 | |||
310 | release_marshal_data(&data); | ||
311 | } | ||
312 | |||
313 | static void | ||
255 | validate_demarshal_u(struct marshal_data *data, | 314 | validate_demarshal_u(struct marshal_data *data, |
256 | struct wl_object *object, uint32_t u) | 315 | struct wl_object *object, uint32_t u) |
257 | { | 316 | { |
@@ -269,7 +328,10 @@ static void | |||
269 | validate_demarshal_s(struct marshal_data *data, | 328 | validate_demarshal_s(struct marshal_data *data, |
270 | struct wl_object *object, const char *s) | 329 | struct wl_object *object, const char *s) |
271 | { | 330 | { |
272 | assert(strcmp(data->value.s, s) == 0); | 331 | if (data->value.s != NULL) |
332 | assert(strcmp(data->value.s, s) == 0); | ||
333 | else | ||
334 | assert(s == NULL); | ||
273 | } | 335 | } |
274 | 336 | ||
275 | static void | 337 | static void |
@@ -343,12 +405,25 @@ TEST(connection_demarshal) | |||
343 | memcpy(&msg[3], data.value.s, msg[2]); | 405 | memcpy(&msg[3], data.value.s, msg[2]); |
344 | demarshal(&data, "s", msg, (void *) validate_demarshal_s); | 406 | demarshal(&data, "s", msg, (void *) validate_demarshal_s); |
345 | 407 | ||
408 | data.value.s = "superdude"; | ||
409 | msg[0] = 400200; | ||
410 | msg[1] = 24; | ||
411 | msg[2] = 10; | ||
412 | memcpy(&msg[3], data.value.s, msg[2]); | ||
413 | demarshal(&data, "?s", msg, (void *) validate_demarshal_s); | ||
414 | |||
346 | data.value.i = wl_fixed_from_double(-90000.2390); | 415 | data.value.i = wl_fixed_from_double(-90000.2390); |
347 | msg[0] = 400200; | 416 | msg[0] = 400200; |
348 | msg[1] = 12; | 417 | msg[1] = 12; |
349 | msg[2] = data.value.i; | 418 | msg[2] = data.value.i; |
350 | demarshal(&data, "f", msg, (void *) validate_demarshal_f); | 419 | demarshal(&data, "f", msg, (void *) validate_demarshal_f); |
351 | 420 | ||
421 | data.value.s = NULL; | ||
422 | msg[0] = 400200; | ||
423 | msg[1] = 12; | ||
424 | msg[2] = 0; | ||
425 | demarshal(&data, "?s", msg, (void *) validate_demarshal_s); | ||
426 | |||
352 | release_marshal_data(&data); | 427 | release_marshal_data(&data); |
353 | } | 428 | } |
354 | 429 | ||
@@ -408,6 +483,10 @@ TEST(connection_marshal_demarshal) | |||
408 | marshal_demarshal(&data, (void *) validate_demarshal_s, | 483 | marshal_demarshal(&data, (void *) validate_demarshal_s, |
409 | 28, "s", data.value.s); | 484 | 28, "s", data.value.s); |
410 | 485 | ||
486 | data.value.s = "cookie robots"; | ||
487 | marshal_demarshal(&data, (void *) validate_demarshal_s, | ||
488 | 28, "?s", data.value.s); | ||
489 | |||
411 | data.value.h = mkstemp(f); | 490 | data.value.h = mkstemp(f); |
412 | assert(data.value.h >= 0); | 491 | assert(data.value.h >= 0); |
413 | marshal_demarshal(&data, (void *) validate_demarshal_h, | 492 | marshal_demarshal(&data, (void *) validate_demarshal_h, |