diff options
Diffstat (limited to 'utils/cec-compliance')
-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"); + } +} |