mirror of https://github.com/Genymobile/scrcpy
Add tests for hid_event_parser & serializer
This also moves the `sc_hid_event` type to a separate file, to enable the other classes to use it without requiring the several other object files to be linked.pull/4556/head
parent
332acab04c
commit
5ff9849765
@ -0,0 +1,17 @@
|
||||
#include "hid_event.h"
|
||||
#include "util/acksync.h"
|
||||
|
||||
void
|
||||
sc_hid_event_init(struct sc_hid_event *hid_event, uint16_t accessory_id,
|
||||
unsigned char *buffer, uint16_t buffer_size) {
|
||||
hid_event->accessory_id = accessory_id;
|
||||
hid_event->buffer = buffer;
|
||||
hid_event->size = buffer_size;
|
||||
hid_event->ack_to_wait = SC_SEQUENCE_INVALID;
|
||||
hid_event->timestamp = 0;
|
||||
}
|
||||
|
||||
void
|
||||
sc_hid_event_destroy(struct sc_hid_event *hid_event) {
|
||||
free(hid_event->buffer);
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
#ifndef SC_HID_EVENT_H
|
||||
#define SC_HID_EVENT_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include "util/tick.h"
|
||||
|
||||
struct sc_hid_event {
|
||||
uint16_t accessory_id;
|
||||
unsigned char *buffer;
|
||||
uint16_t size;
|
||||
uint64_t ack_to_wait;
|
||||
sc_tick timestamp; // Only used by hid_replay.c & hid_event_serializer.c
|
||||
};
|
||||
|
||||
// Takes ownership of buffer
|
||||
void
|
||||
sc_hid_event_init(struct sc_hid_event *hid_event, uint16_t accessory_id,
|
||||
unsigned char *buffer, uint16_t buffer_size);
|
||||
|
||||
void
|
||||
sc_hid_event_destroy(struct sc_hid_event *hid_event);
|
||||
#endif
|
@ -0,0 +1,290 @@
|
||||
#include "common.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "usb/hid_event_serializer.h"
|
||||
|
||||
static void test_hid_event_serializer(void) {
|
||||
struct sc_hid_event hid_event;
|
||||
uint16_t accessory_id = 1337;
|
||||
unsigned char *buffer = malloc(5);
|
||||
buffer[0] = '\xDE';
|
||||
buffer[1] = '\xEA';
|
||||
buffer[2] = '\xBE';
|
||||
buffer[3] = '\xEF';
|
||||
buffer[4] = '\x00';
|
||||
uint16_t buffer_size = 5;
|
||||
sc_hid_event_init(&hid_event, accessory_id, buffer, buffer_size);
|
||||
assert(hid_event.timestamp == 0);
|
||||
|
||||
struct sc_hid_event_serializer hes;
|
||||
sc_hid_event_serializer_init(&hes);
|
||||
|
||||
assert(hes.data_len == 0);
|
||||
|
||||
sc_hid_event_serializer_update(&hes, &hid_event);
|
||||
assert(strlen("0 1337 de ea be ef 00\n") == hes.data_len);
|
||||
assert(hes.data_len < hes.data_buffer_size); // Need room for NUL.
|
||||
assert(!strncmp("0 1337 de ea be ef 00\n", hes.data, hes.data_len + 1));
|
||||
|
||||
hid_event.timestamp = 9001;
|
||||
sc_hid_event_serializer_update(&hes, &hid_event);
|
||||
assert(!strncmp("0 1337 de ea be ef 00\n9001 1337 de ea be ef 00\n",
|
||||
hes.data, hes.data_len + 1));
|
||||
|
||||
sc_hid_event_serializer_mark_as_read(&hes);
|
||||
assert(hes.data_len == 0);
|
||||
|
||||
sc_hid_event_serializer_update(&hes, &hid_event);
|
||||
assert(!strncmp("9001 1337 de ea be ef 00\n", hes.data, hes.data_len + 1));
|
||||
|
||||
sc_hid_event_serializer_destroy(&hes);
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
static void test_hid_event_serializer_only_init_and_destroy(void) {
|
||||
struct sc_hid_event_serializer hes;
|
||||
sc_hid_event_serializer_init(&hes);
|
||||
sc_hid_event_serializer_destroy(&hes);
|
||||
}
|
||||
|
||||
static void test_hid_event_serializer_minimum_length(void) {
|
||||
struct sc_hid_event hid_event;
|
||||
sc_hid_event_init(&hid_event, 2, calloc(1, 1), 1);
|
||||
assert(hid_event.timestamp == 0);
|
||||
|
||||
struct sc_hid_event_serializer hes;
|
||||
sc_hid_event_serializer_init(&hes);
|
||||
sc_hid_event_serializer_update(&hes, &hid_event);
|
||||
|
||||
assert(hes.data_len < hes.data_buffer_size); // Need room for NUL.
|
||||
assert(strlen("0 2 00\n") == hes.data_len);
|
||||
assert(!strncmp("0 2 00\n", hes.data, hes.data_len + 1));
|
||||
|
||||
sc_hid_event_serializer_destroy(&hes);
|
||||
free(hid_event.buffer);
|
||||
}
|
||||
|
||||
static void test_hid_event_serializer_maximum_length(void) {
|
||||
struct sc_hid_event hid_event;
|
||||
sc_hid_event_init(&hid_event, 65535, calloc(1, 1), 1);
|
||||
// As the type is a signed 64-bit integer, the largest length is its lowest
|
||||
// value. It is unlikely for such a timestamp to be seen in practice.
|
||||
hid_event.timestamp = -9223372036854775807; // = -(2^63-1)
|
||||
|
||||
struct sc_hid_event_serializer hes;
|
||||
sc_hid_event_serializer_init(&hes);
|
||||
sc_hid_event_serializer_update(&hes, &hid_event);
|
||||
|
||||
// Now perform an exact check instead of a "data_len < buffer_size". The
|
||||
// minimum buffer size is carefully chosen to fit the longest values.
|
||||
assert(hes.data_len + 1 == hes.data_buffer_size);
|
||||
|
||||
assert(hes.data_len < hes.data_buffer_size); // Need room for NUL.
|
||||
assert(strlen("-9223372036854775807 65535 00\n") == hes.data_len);
|
||||
assert(!strncmp("-9223372036854775807 65535 00\n", hes.data,
|
||||
hes.data_len + 1));
|
||||
|
||||
sc_hid_event_serializer_destroy(&hes);
|
||||
free(hid_event.buffer);
|
||||
}
|
||||
|
||||
static void test_hid_event_parser(void) {
|
||||
struct sc_hid_event_parser hep;
|
||||
sc_hid_event_parser_init(&hep, "source_name");
|
||||
|
||||
const char input_str[] = "1 1023 f0 0d\n";
|
||||
int input_len = strlen(input_str);
|
||||
// Note: allocate just enough to hold input_str, without trailing NUL byte,
|
||||
// to show that the NUL byte is not required (even though C strings will
|
||||
// always end with a NUL).
|
||||
char *data = malloc(input_len);
|
||||
memcpy(data, input_str, input_len);
|
||||
bool ok = sc_hid_event_parser_append_data(&hep, data, input_len);
|
||||
assert(ok);
|
||||
free(data); // Free immediately, further operations should not trigger UAF.
|
||||
|
||||
sc_hid_event_parser_parse_all_data(&hep);
|
||||
assert(sc_hid_event_parser_has_next(&hep));
|
||||
struct sc_hid_event *parsed_event = sc_hid_event_parser_get_next(&hep);
|
||||
assert(parsed_event);
|
||||
assert(!sc_hid_event_parser_has_error(&hep));
|
||||
assert(!sc_hid_event_parser_has_unparsed_data(&hep));
|
||||
|
||||
assert(parsed_event->timestamp == 1);
|
||||
assert(parsed_event->accessory_id == 1023);
|
||||
assert(parsed_event->size == 2);
|
||||
assert(!strncmp((const char*)parsed_event->buffer, "\xf0\x0d", 2));
|
||||
|
||||
// Clean up.
|
||||
sc_hid_event_destroy(parsed_event);
|
||||
free(parsed_event);
|
||||
|
||||
// Another one, partial.
|
||||
ok = sc_hid_event_parser_append_data(&hep, "7", 1);
|
||||
assert(ok);
|
||||
sc_hid_event_parser_parse_all_data(&hep);
|
||||
assert(!sc_hid_event_parser_has_next(&hep));
|
||||
parsed_event = sc_hid_event_parser_get_next(&hep);
|
||||
assert(!parsed_event); // Incomplete.
|
||||
assert(!sc_hid_event_parser_has_error(&hep));
|
||||
assert(sc_hid_event_parser_has_unparsed_data(&hep));
|
||||
|
||||
// Append part of the original line (minus '\n')
|
||||
ok = sc_hid_event_parser_append_data(&hep, input_str, input_len - 1);
|
||||
assert(ok);
|
||||
// Append the new ending.
|
||||
ok = sc_hid_event_parser_append_data(&hep, " DE ED\n", strlen(" DE ED\n"));
|
||||
assert(ok);
|
||||
|
||||
// The event is not seen until sc_hid_event_parser_parse_all_data() is run:
|
||||
parsed_event = sc_hid_event_parser_get_next(&hep);
|
||||
assert(!parsed_event);
|
||||
|
||||
sc_hid_event_parser_parse_all_data(&hep);
|
||||
parsed_event = sc_hid_event_parser_get_next(&hep);
|
||||
assert(parsed_event);
|
||||
assert(!sc_hid_event_parser_has_error(&hep));
|
||||
assert(!sc_hid_event_parser_has_unparsed_data(&hep));
|
||||
|
||||
assert(parsed_event->timestamp == 71);
|
||||
assert(parsed_event->accessory_id == 1023);
|
||||
assert(parsed_event->size == 4);
|
||||
assert(!strncmp((const char*)parsed_event->buffer, "\xf0\x0d\xde\xed", 4));
|
||||
|
||||
sc_hid_event_parser_destroy(&hep);
|
||||
|
||||
// Clean up once more, now after hep has been destroyed to prove that the
|
||||
// parsed_event outlives the parser.
|
||||
sc_hid_event_destroy(parsed_event);
|
||||
free(parsed_event);
|
||||
}
|
||||
|
||||
static void test_hid_event_parser_only_init_and_destroy(void) {
|
||||
struct sc_hid_event_parser hep;
|
||||
sc_hid_event_parser_init(&hep, "source_name");
|
||||
sc_hid_event_parser_destroy(&hep);
|
||||
}
|
||||
|
||||
static void test_hid_event_parser_reject_null_in_input(void) {
|
||||
struct sc_hid_event_parser hep;
|
||||
sc_hid_event_parser_init(&hep, "invalid_embedded_nulls");
|
||||
|
||||
const size_t input_len = 14;
|
||||
char input_with_nul[15] = {0};
|
||||
memcpy(input_with_nul + 1, "1 1023 f0 0d\n", 14);
|
||||
|
||||
// Note: data is not empty but data_len is 0, so the \x00 should be ignored.
|
||||
bool ok = sc_hid_event_parser_append_data(&hep, input_with_nul, 0);
|
||||
assert(ok); // No data to append, all right!
|
||||
sc_hid_event_parser_parse_all_data(&hep);
|
||||
assert(!sc_hid_event_parser_get_next(&hep));
|
||||
assert(!sc_hid_event_parser_has_error(&hep));
|
||||
assert(!sc_hid_event_parser_has_unparsed_data(&hep));
|
||||
|
||||
ok = sc_hid_event_parser_append_data(&hep, input_with_nul, input_len);
|
||||
assert(!ok); // Invalid due to null.
|
||||
// The error is not propagated until sc_hid_event_parser_parse_all_data():
|
||||
assert(!sc_hid_event_parser_has_error(&hep));
|
||||
// The "has unparsed data" status is immediately updated.
|
||||
assert(sc_hid_event_parser_has_unparsed_data(&hep));
|
||||
|
||||
sc_hid_event_parser_parse_all_data(&hep);
|
||||
|
||||
// Confirm that once an error is reached, that parsing fails too.
|
||||
assert(!sc_hid_event_parser_get_next(&hep));
|
||||
assert(sc_hid_event_parser_has_error(&hep));
|
||||
assert(sc_hid_event_parser_has_unparsed_data(&hep));
|
||||
|
||||
sc_hid_event_parser_destroy(&hep);
|
||||
}
|
||||
|
||||
static void test_hid_event_parser_invalid_data(void) {
|
||||
struct sc_hid_event_parser hep;
|
||||
sc_hid_event_parser_init(&hep, "invalid_source_data");
|
||||
|
||||
bool ok = sc_hid_event_parser_append_data(&hep, "", 0);
|
||||
assert(ok); // No data to append, all right!
|
||||
sc_hid_event_parser_parse_all_data(&hep);
|
||||
assert(!sc_hid_event_parser_get_next(&hep));
|
||||
assert(!sc_hid_event_parser_has_error(&hep));
|
||||
assert(!sc_hid_event_parser_has_unparsed_data(&hep));
|
||||
|
||||
ok = sc_hid_event_parser_append_data(&hep, "", 0);
|
||||
assert(ok); // No data to append, still all right!
|
||||
sc_hid_event_parser_parse_all_data(&hep);
|
||||
assert(!sc_hid_event_parser_get_next(&hep));
|
||||
assert(!sc_hid_event_parser_has_error(&hep));
|
||||
assert(!sc_hid_event_parser_has_unparsed_data(&hep));
|
||||
|
||||
ok = sc_hid_event_parser_append_data(&hep, "Clearly bogus\n", 14);
|
||||
assert(ok); // Garbage accepted - append does not validate.
|
||||
sc_hid_event_parser_parse_all_data(&hep);
|
||||
assert(!sc_hid_event_parser_get_next(&hep));
|
||||
assert(sc_hid_event_parser_has_error(&hep));
|
||||
assert(sc_hid_event_parser_has_unparsed_data(&hep));
|
||||
|
||||
ok = sc_hid_event_parser_append_data(&hep, "", 0);
|
||||
assert(!ok); // No more data accepted when garbage is encountered.
|
||||
|
||||
sc_hid_event_parser_destroy(&hep);
|
||||
}
|
||||
|
||||
static void test_hid_event_parser_skips_comments_and_lines(void) {
|
||||
struct sc_hid_event_parser hep;
|
||||
sc_hid_event_parser_init(&hep, "source_name");
|
||||
|
||||
bool ok = sc_hid_event_parser_append_data(&hep, "\n\n\n\n\n", 5);
|
||||
assert(ok);
|
||||
assert(hep.line == 1); // Not parsed yet.
|
||||
sc_hid_event_parser_parse_all_data(&hep);
|
||||
assert(hep.line == 6); // Skipped all blank lines.
|
||||
assert(!sc_hid_event_parser_get_next(&hep));
|
||||
assert(!sc_hid_event_parser_has_error(&hep));
|
||||
assert(!sc_hid_event_parser_has_unparsed_data(&hep));
|
||||
|
||||
ok = sc_hid_event_parser_append_data(&hep, "#", 1);
|
||||
assert(ok);
|
||||
sc_hid_event_parser_parse_all_data(&hep);
|
||||
assert(hep.line == 6); // Line not changed.
|
||||
assert(!sc_hid_event_parser_get_next(&hep));
|
||||
assert(!sc_hid_event_parser_has_error(&hep));
|
||||
assert(sc_hid_event_parser_has_unparsed_data(&hep));
|
||||
|
||||
ok = sc_hid_event_parser_append_data(&hep, "xxx\n", 4);
|
||||
assert(ok);
|
||||
assert(hep.line == 6); // Not parsed yet.
|
||||
sc_hid_event_parser_parse_all_data(&hep);
|
||||
assert(hep.line == 7); // Line consumed & ignored.
|
||||
assert(!sc_hid_event_parser_get_next(&hep));
|
||||
assert(!sc_hid_event_parser_has_error(&hep));
|
||||
assert(!sc_hid_event_parser_has_unparsed_data(&hep));
|
||||
|
||||
ok = sc_hid_event_parser_append_data(&hep, "#\n", 2);
|
||||
assert(ok);
|
||||
sc_hid_event_parser_parse_all_data(&hep);
|
||||
assert(hep.line == 8); // # immediately followed by \n is also ignored.
|
||||
assert(!sc_hid_event_parser_get_next(&hep));
|
||||
assert(!sc_hid_event_parser_has_error(&hep));
|
||||
assert(!sc_hid_event_parser_has_unparsed_data(&hep));
|
||||
|
||||
sc_hid_event_parser_destroy(&hep);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
|
||||
test_hid_event_serializer();
|
||||
test_hid_event_serializer_only_init_and_destroy();
|
||||
test_hid_event_serializer_minimum_length();
|
||||
test_hid_event_serializer_maximum_length();
|
||||
|
||||
test_hid_event_parser();
|
||||
test_hid_event_parser_only_init_and_destroy();
|
||||
test_hid_event_parser_reject_null_in_input();
|
||||
test_hid_event_parser_invalid_data();
|
||||
test_hid_event_parser_skips_comments_and_lines();
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue