summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVadim Bendebury <vbendeb@chromium.org>2017-02-20 12:15:52 -0800
committerYoucheng Syu <youcheng@google.com>2017-03-07 06:00:09 +0000
commit4cef29e5f6afa0a8171d1349436cd8b62dc243d1 (patch)
tree43e3bd30b0dfc17cdea3a45209de96598e0bc3d5
parentb74fe8627b9b3efc06c427df12cf6631f1d28b5c (diff)
downloadchrome-ec-4cef29e5f6afa0a8171d1349436cd8b62dc243d1.tar.gz
g: rate limit firmware updates
This patch introduces a delay between accepted cr50 firmware upload attempts. The next attempt to write into the same or lower address in flash would be accepted no sooner than in 60 seconds after the previous attempt. This would prevent a rogue user from wearing the flash by repeated uploads to the same address. This limitation is not imposed by dev images (those compiled with CR50_DEV=1). BRANCH=none BUG=chrome-os-partner:63098 TEST=verified that attempts to update soon after the previous update result in the following error message issued by usb_updater: sending 0x2d8b8 bytes to 0x4000 Error: status 0x9 Modified usb_updater to send one random pdu twice. Observed the same error message. Change-Id: Idca55ad091d09daaddd0a4cad5b1f871af1ede93 Signed-off-by: Vadim Bendebury <vbendeb@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/445496 Reviewed-by: Randall Spangler <rspangler@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/451197 Reviewed-by: Youcheng Syu <youcheng@google.com> Commit-Queue: Youcheng Syu <youcheng@google.com> Tested-by: Youcheng Syu <youcheng@google.com>
-rw-r--r--chip/g/config_chip.h2
-rw-r--r--chip/g/upgrade_fw.c141
-rw-r--r--chip/g/upgrade_fw.h8
-rw-r--r--extra/usb_updater/usb_updater.c1
4 files changed, 147 insertions, 5 deletions
diff --git a/chip/g/config_chip.h b/chip/g/config_chip.h
index ad5f95cf4d..1a8b24fca8 100644
--- a/chip/g/config_chip.h
+++ b/chip/g/config_chip.h
@@ -95,7 +95,7 @@
* use these two areas for the same thing, it's just more convenient to make
* them the same size.
*/
-#define CFG_TOP_SIZE 0x4000
+#define CFG_TOP_SIZE 0x3000
#define CFG_TOP_A_OFF (CFG_FLASH_HALF - CFG_TOP_SIZE)
#define CFG_TOP_B_OFF (CONFIG_FLASH_SIZE - CFG_TOP_SIZE)
diff --git a/chip/g/upgrade_fw.c b/chip/g/upgrade_fw.c
index d5be0f4491..182f1308fc 100644
--- a/chip/g/upgrade_fw.c
+++ b/chip/g/upgrade_fw.c
@@ -4,13 +4,14 @@
*/
#include "byteorder.h"
+#include "compile_time_macros.h"
#include "console.h"
#include "dcrypto/dcrypto.h"
#include "extension.h"
#include "flash.h"
#include "hooks.h"
-#include "include/compile_time_macros.h"
#include "system.h"
+#include "system_chip.h"
#include "registers.h"
#include "uart.h"
@@ -169,6 +170,129 @@ int usb_pdu_valid(struct upgrade_command *cmd_body, size_t cmd_size)
return 1;
}
+#ifndef CR50_DEV
+
+/* Compare two versions, return True if the new version is older. */
+static int new_is_older(const struct SignedHeader *new,
+ const struct SignedHeader *old)
+{
+ if (new->epoch_ != old->epoch_)
+ return new->epoch_ < old->epoch_;
+
+ if (new->major_ != old->major_)
+ return new->major_ < old->major_;
+
+
+ return new->minor_ < old->minor_;
+}
+
+/*
+ * Check if this chunk of data is a rollback attempt, or is unaligned and
+ * overlaps RO or RW header.
+ *
+ * Return False if this is such an attempt or an overlap, when in prod mode;
+ * otherwise return True.
+ */
+static int contents_allowed(uint32_t block_offset,
+ size_t body_size, void *upgrade_data)
+{
+ /* Pointer to RO or RW header in flash, to compare against. */
+ const struct SignedHeader *header;
+
+ if (block_offset == valid_sections.ro_base_offset) {
+ header = (const struct SignedHeader *)
+ get_program_memory_addr(system_get_ro_image_copy());
+ } else if (block_offset == valid_sections.rw_base_offset) {
+ header = (const struct SignedHeader *)
+ get_program_memory_addr(system_get_image_copy());
+ } else {
+
+ /*
+ * The received block is not destined to a header directly,
+ * but does it overlap with a header by any chance?
+ */
+ int i;
+ /* Base offsets of valid headers in flash. */
+ uint32_t bases[] = { valid_sections.ro_base_offset,
+ valid_sections.rw_base_offset };
+ /* Range of offsets this block is covering. */
+ uint32_t range[] = { block_offset, block_offset + body_size };
+
+ for (i = 0; i < ARRAY_SIZE(bases); i++) {
+ int j;
+
+ for (j = 0; j < ARRAY_SIZE(range); j++) {
+ if ((range[j] >= bases[i]) &&
+ (range[j] <
+ (bases[i] +
+ sizeof(struct SignedHeader)))) {
+ CPRINTF("%s:"
+ " unaligned block overlaps\n",
+ __func__);
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+ }
+
+ /* This block is a header (ro or rw) of the new image. */
+ if (body_size < sizeof(struct SignedHeader)) {
+ CPRINTF("%s: block too short\n", __func__);
+ return 0;
+ }
+
+ /* upgrade_data is the new header. */
+ if (new_is_older(upgrade_data, header)) {
+ CPRINTF("%s: rejecting an older header.\n", __func__);
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static uint32_t prev_offset;
+static uint64_t prev_timestamp;
+#define BACKOFF_TIME (60 * SECOND)
+
+static int chunk_came_too_soon(uint32_t block_offset)
+{
+ if (!prev_timestamp ||
+ ((get_time().val - prev_timestamp) > BACKOFF_TIME))
+ return 0;
+
+ if (!prev_offset ||
+ (block_offset >= (prev_offset + SIGNED_TRANSFER_SIZE)))
+ return 0;
+
+ CPRINTF("%s: rejecting a write to the same block\n", __func__);
+ return 1;
+}
+
+static void new_chunk_written(uint32_t block_offset)
+{
+ prev_timestamp = get_time().val;
+ prev_offset = block_offset;
+}
+#else
+static int chunk_came_too_soon(uint32_t block_offset)
+{
+ return 0;
+}
+
+static void new_chunk_written(uint32_t block_offset)
+{
+}
+
+static int contents_allowed(uint32_t block_offset,
+ size_t body_size, void *upgrade_data)
+{
+ return 1;
+}
+#endif
+
void fw_upgrade_command_handler(void *body,
size_t cmd_size,
size_t *response_size)
@@ -252,14 +376,24 @@ void fw_upgrade_command_handler(void *body,
return;
}
+ upgrade_data = cmd_body + 1;
+ if (!contents_allowed(block_offset, body_size, upgrade_data)) {
+ *error_code = UPGRADE_ROLLBACK_ERROR;
+ return;
+ }
+
/* Check if the block will fit into the valid area. */
*error_code = check_update_chunk(block_offset, body_size);
if (*error_code)
return;
+ if (chunk_came_too_soon(block_offset)) {
+ *error_code = UPGRADE_RATE_LIMIT_ERROR;
+ return;
+ }
+
CPRINTF("%s: programming at address 0x%x\n", __func__,
block_offset + CONFIG_PROGRAM_MEMORY_BASE);
- upgrade_data = cmd_body + 1;
if (flash_physical_write(block_offset, body_size, upgrade_data)
!= EC_SUCCESS) {
*error_code = UPGRADE_WRITE_FAILURE;
@@ -267,6 +401,8 @@ void fw_upgrade_command_handler(void *body,
return;
}
+ new_chunk_written(block_offset);
+
/* Verify that data was written properly. */
if (memcmp(upgrade_data, (void *)
(block_offset + CONFIG_PROGRAM_MEMORY_BASE),
@@ -284,4 +420,3 @@ void fw_upgrade_complete(void)
{
system_clear_retry_counter();
}
-
diff --git a/chip/g/upgrade_fw.h b/chip/g/upgrade_fw.h
index f91e684e38..092f2812fa 100644
--- a/chip/g/upgrade_fw.h
+++ b/chip/g/upgrade_fw.h
@@ -136,6 +136,14 @@ enum return_value {
UPGRADE_VERIFY_ERROR = 5,
UPGRADE_GEN_ERROR = 6,
UPGRADE_MALLOC_ERROR = 7,
+ UPGRADE_ROLLBACK_ERROR = 8,
+ UPGRADE_RATE_LIMIT_ERROR = 9,
};
+/*
+ * This is the size of the update frame payload, unless this is the last chunk
+ * of the image.
+ */
+#define SIGNED_TRANSFER_SIZE 1024
+
#endif /* ! __EC_CHIP_G_UPGRADE_FW_H */
diff --git a/extra/usb_updater/usb_updater.c b/extra/usb_updater/usb_updater.c
index e41c3027b1..50d7cfaf3c 100644
--- a/extra/usb_updater/usb_updater.c
+++ b/extra/usb_updater/usb_updater.c
@@ -179,7 +179,6 @@ struct upgrade_pkt {
char data[0];
} __packed;
-#define SIGNED_TRANSFER_SIZE 1024
#define MAX_BUF_SIZE (SIGNED_TRANSFER_SIZE + sizeof(struct upgrade_pkt))
struct usb_endpoint {