diff options
author | Hans Verkuil <hverkuil-cisco@xs4all.nl> | 2019-09-30 13:35:25 +0200 |
---|---|---|
committer | Hans Verkuil <hverkuil-cisco@xs4all.nl> | 2019-09-30 13:35:25 +0200 |
commit | 6c9c63d98d60a4478d0e2a2f45fe0e54793f5582 (patch) | |
tree | 86ba59d8ba767dc4cbf5337304104bc7d52d0d48 /utils | |
parent | 9b773c22aee1b871031f3455c519fabb7e789585 (diff) |
cec-compliance: add --test-fuzzing option
Add fuzzing support. Randomly generate CEC messages. After every
10 random messages check that you can still get the CEC version from
the remote device.
This is an initial implementation.
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Diffstat (limited to 'utils')
-rw-r--r-- | utils/cec-compliance/Makefile.am | 2 | ||||
-rw-r--r-- | utils/cec-compliance/cec-compliance.1.in | 4 | ||||
-rw-r--r-- | utils/cec-compliance/cec-compliance.cpp | 15 | ||||
-rw-r--r-- | utils/cec-compliance/cec-compliance.h | 8 | ||||
-rw-r--r-- | utils/cec-compliance/cec-test-fuzzing.cpp | 116 |
5 files changed, 142 insertions, 3 deletions
diff --git a/utils/cec-compliance/Makefile.am b/utils/cec-compliance/Makefile.am index 0c18a9ab..958b2f9d 100644 --- a/utils/cec-compliance/Makefile.am +++ b/utils/cec-compliance/Makefile.am @@ -1,7 +1,7 @@ bin_PROGRAMS = cec-compliance man_MANS = cec-compliance.1 -cec_compliance_SOURCES = cec-compliance.cpp cec-compliance.h cec-test.cpp cec-test-adapter.cpp cec-test-audio.cpp cec-test-power.cpp cec-info.cpp +cec_compliance_SOURCES = cec-compliance.cpp cec-compliance.h cec-test.cpp cec-test-adapter.cpp cec-test-audio.cpp cec-test-power.cpp cec-test-fuzzing.cpp cec-info.cpp cec_compliance_CPPFLAGS = -I$(top_srcdir)/utils/common cec_compliance_LDFLAGS = -lrt diff --git a/utils/cec-compliance/cec-compliance.1.in b/utils/cec-compliance/cec-compliance.1.in index fa96fb01..0f4ebf44 100644 --- a/utils/cec-compliance/cec-compliance.1.in +++ b/utils/cec-compliance/cec-compliance.1.in @@ -129,6 +129,10 @@ Set the standby/resume timeout to the given number of seconds. Default is 60s. \fB\-A\fR, \fB\-\-test\-adapter\fR Test the CEC adapter API .TP +\fB\-F\fR, \fB\-\-test\-fuzzing\fR +Test the remote CEC adapter by randomly creating CEC messages. +This runs forever until an error occurs. +.TP \fB\-\-test\-core\fR Test the core functionality .TP diff --git a/utils/cec-compliance/cec-compliance.cpp b/utils/cec-compliance/cec-compliance.cpp index 756d8dbf..faaaa432 100644 --- a/utils/cec-compliance/cec-compliance.cpp +++ b/utils/cec-compliance/cec-compliance.cpp @@ -40,6 +40,7 @@ enum Option { OptSetDevice = 'd', OptSetDriver = 'D', OptExitOnFail = 'E', + OptTestFuzzing = 'F', OptHelp = 'h', OptInteractive = 'i', OptNoWarnings = 'n', @@ -129,6 +130,7 @@ static struct option long_options[] = { {"reply-threshold", required_argument, 0, OptReplyThreshold}, {"test-adapter", no_argument, 0, OptTestAdapter}, + {"test-fuzzing", no_argument, 0, OptTestFuzzing}, {"test-core", no_argument, 0, OptTestCore}, {"test-audio-rate-control", no_argument, 0, OptTestAudioRateControl}, {"test-audio-return-channel-control", no_argument, 0, OptTestARCControl}, @@ -186,6 +188,7 @@ static void usage(void) " -t, --timeout <secs> Set the standby/resume timeout to <secs>. Default is 60s.\n" "\n" " -A, --test-adapter Test the CEC adapter API\n" + " -F, --test-fuzzing Test by fuzzing CEC messages\n" " --test-core Test the core functionality\n" "\n" "By changing --test to --skip-test in the following options you can skip tests\n" @@ -586,6 +589,15 @@ const char *cdc_errcode2s(__u8 cdc_errcode) } } +const char *opcode2s(__u8 opcode) +{ + for (unsigned i = 0; i < ARRAY_SIZE(msgtable); i++) { + if (msgtable[i].opcode == opcode) + return msgtable[i].name; + } + return NULL; +} + std::string opcode2s(const struct cec_msg *msg) { std::stringstream oss; @@ -1399,6 +1411,9 @@ int main(int argc, char **argv) topology_probe_device(&node, i, node.log_addr[0]); printf("\n"); + if (options[OptTestFuzzing] && remote_la >= 0) + exit(testFuzzing(node, laddrs.log_addr[0], remote_la)); + unsigned remote_la_mask = node.remote_la_mask; if (remote_la >= 0) diff --git a/utils/cec-compliance/cec-compliance.h b/utils/cec-compliance/cec-compliance.h index 023c34d4..9468f535 100644 --- a/utils/cec-compliance/cec-compliance.h +++ b/utils/cec-compliance/cec-compliance.h @@ -297,6 +297,7 @@ int cec_named_ioctl(struct node *node, const char *name, #define doioctl(n, r, p) cec_named_ioctl(n, #r, r, p) +const char *opcode2s(__u8 opcode); std::string opcode2s(const struct cec_msg *msg); static inline bool is_tv(unsigned la, unsigned prim_type) @@ -400,6 +401,9 @@ int testLostMsgs(struct node *node); void testAdapter(struct node &node, struct cec_log_addrs &laddrs, const char *device); +// CEC fuzzing test +int testFuzzing(struct node &node, unsigned me, unsigned la); + // CEC core tests int testCore(struct node *node); int core_unknown(struct node *node, unsigned me, unsigned la, bool interactive); @@ -419,7 +423,7 @@ int testProcessing(struct node *node, unsigned me); void testRemote(struct node *node, unsigned me, unsigned la, unsigned test_tags, bool interactive); -// cec-audio.cpp +// cec-test-audio.cpp extern struct remote_subtest sac_subtests[]; extern const unsigned sac_subtests_size; extern struct remote_subtest dal_subtests[]; @@ -429,7 +433,7 @@ extern const unsigned arc_subtests_size; extern struct remote_subtest audio_rate_ctl_subtests[]; extern const unsigned audio_rate_ctl_subtests_size; -// cec-power.cpp +// cec-test-power.cpp bool util_interactive_ensure_power_state(struct node *node, unsigned me, unsigned la, bool interactive, __u8 target_pwr); extern struct remote_subtest standby_subtests[]; diff --git a/utils/cec-compliance/cec-test-fuzzing.cpp b/utils/cec-compliance/cec-test-fuzzing.cpp new file mode 100644 index 00000000..fe3c8f21 --- /dev/null +++ b/utils/cec-compliance/cec-test-fuzzing.cpp @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + */ + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <ctype.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <config.h> +#include <sstream> + +#include "cec-compliance.h" + +int testFuzzing(struct node &node, unsigned me, unsigned la) +{ + printf("test fuzzing CEC local LA %d (%s) to remote LA %d (%s):\n\n", + me, la2s(me), la, la2s(la)); + + if (node.remote[la].in_standby) { + announce("The remote device is in standby. It should be powered on when fuzzing. Aborting."); + return 0; + } + if (!node.remote[la].has_power_status) { + announce("The device didn't support Give Device Power Status."); + announce("Assuming that the device is powered on."); + } + + unsigned int cnt = 0; + + for (;;) { + cec_msg msg; + __u8 cmd; + unsigned offset = 2; + + cec_msg_init(&msg, me, la); + msg.msg[1] = cmd = random() & 0xff; + if (msg.msg[1] == CEC_MSG_STANDBY) + continue; + msg.len = (random() & 0xf) + 2; + if (msg.msg[1] == CEC_MSG_VENDOR_COMMAND_WITH_ID && + node.remote[la].vendor_id != CEC_VENDOR_ID_NONE) { + msg.len += 3; + offset += 3; + msg.msg[2] = (node.remote[la].vendor_id & 0xff0000) >> 16; + msg.msg[3] = (node.remote[la].vendor_id & 0xff00) >> 8; + msg.msg[4] = node.remote[la].vendor_id & 0xff; + } + if (msg.len > CEC_MAX_MSG_SIZE) + continue; + + const char *name = opcode2s(msg.msg[1]); + + printf("Send message %u:", cnt); + for (unsigned int i = 0; i < offset; i++) + printf(" %02x", msg.msg[i]); + for (unsigned int i = offset; i < msg.len; i++) { + msg.msg[i] = random() & 0xff; + printf(" %02x", msg.msg[i]); + } + if (name) + printf(" (%s)", name); + printf(": "); + msg.reply = CEC_MSG_FEATURE_ABORT; + fail_on_test(!transmit_timeout(&node, &msg, 1200)); + printf("%s", timed_out(&msg) ? "Timed out" : "Feature Abort"); + + if (cec_msg_status_is_abort(&msg)) { + __u8 abort_msg, reason; + + cec_ops_feature_abort(&msg, &abort_msg, &reason); + fail_on_test(abort_msg != cmd); + switch (reason) { + case CEC_OP_ABORT_UNRECOGNIZED_OP: + printf(" (Unrecognized Op)"); + break; + case CEC_OP_ABORT_UNDETERMINED: + printf(" (Undetermined)"); + break; + case CEC_OP_ABORT_INVALID_OP: + printf(" (Invalid Op)"); + break; + case CEC_OP_ABORT_NO_SOURCE: + printf(" (No Source)"); + break; + case CEC_OP_ABORT_REFUSED: + printf(" (Refused)"); + break; + case CEC_OP_ABORT_INCORRECT_MODE: + printf(" (Incorrect Mode)"); + break; + default: + printf(" (0x%02x)\n", reason); + fail("Invalid reason\n"); + break; + } + } + printf("\n"); + if (++cnt % 10) + continue; + if (la == CEC_LOG_ADDR_BROADCAST) + continue; + cec_msg_init(&msg, me, la); + cec_msg_get_cec_version(&msg, true); + printf("Query CEC Version: "); + fail_on_test(!transmit_timeout(&node, &msg) || timed_out_or_abort(&msg)); + printf("OK\n"); + } +} |