Skip to content

Commit

Permalink
firmware-versions: Add test case for parsing VERSION
Browse files Browse the repository at this point in the history
Also make it possible to use with afl-lop/afl-fuzz just to help make
*sure* we're all good.

Additionally, if we hit a entry in VERSION that is larger than our
buffer size, we skip over it gracefully rather than overwriting the
stack. This is only a problem if VERSION isn't trusted, which as of
4b8cc05 it is verified as part of
Secure Boot.

CC: stable # v5.9+
Fixes: 9727fe3
[stewart: fix up include ordering for building on centos7]
Signed-off-by: Stewart Smith <stewart@linux.ibm.com>
  • Loading branch information
stewartsmith committed Feb 13, 2019
1 parent abd17b2 commit 3170270
Show file tree
Hide file tree
Showing 18 changed files with 364 additions and 147 deletions.
1 change: 1 addition & 0 deletions core/Makefile.inc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ CORE_OBJS += console-log.o ipmi.o time-utils.o pel.o pool.o errorlog.o
CORE_OBJS += timer.o i2c.o rtc.o flash.o sensor.o ipmi-opal.o
CORE_OBJS += flash-subpartition.o bitmap.o buddy.o pci-quirk.o powercap.o psr.o
CORE_OBJS += pci-dt-slot.o direct-controls.o cpufeatures.o
CORE_OBJS += flash-firmware-versions.o

ifeq ($(SKIBOOT_GCOV),1)
CORE_OBJS += gcov-profiling.o
Expand Down
173 changes: 173 additions & 0 deletions core/flash-firmware-versions.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
/* Copyright 2013-2018 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <skiboot.h>
#include <device.h>
#include <opal.h>
#include <libstb/secureboot.h>
#include <libstb/trustedboot.h>

/* ibm,firmware-versions support */
static char *version_buf;
static size_t version_buf_size = 0x2000;

static void __flash_dt_add_fw_version(struct dt_node *fw_version, char* data)
{
static bool first = true;
char *prop;
int version_len, i;
int len = strlen(data);
const char *skiboot_version;
const char * version_str[] = {"open-power", "buildroot", "skiboot",
"hostboot-binaries", "hostboot", "linux",
"petitboot", "occ", "capp-ucode", "sbe",
"machine-xml", "hcode"};

if (first) {
first = false;

/* Increment past "key-" */
if (memcmp(data, "open-power", strlen("open-power")) == 0)
prop = data + strlen("open-power");
else
prop = strchr(data, '-');
if (!prop) {
prlog(PR_DEBUG,
"FLASH: Invalid fw version format (%s)\n", data);
return;
}
prop++;

dt_add_property_string(fw_version, "version", prop);
return;
}

/*
* PNOR version strings are not easily consumable. Split them into
* property, value.
*
* Example input from PNOR :
* "open-power-firestone-v1.8"
* "linux-4.4.6-openpower1-8420e0f"
*
* Desired output in device tree:
* open-power = "firestone-v1.8";
* linux = "4.4.6-openpower1-8420e0f";
*/
for(i = 0; i < ARRAY_SIZE(version_str); i++)
{
version_len = strlen(version_str[i]);
if (len < version_len)
continue;

if (memcmp(data, version_str[i], version_len) != 0)
continue;

/* Found a match, add property */
if (dt_find_property(fw_version, version_str[i]))
continue;

/* Increment past "key-" */
prop = data + version_len + 1;
dt_add_property_string(fw_version, version_str[i], prop);

/* Sanity check against what Skiboot thinks its version is. */
if (strncmp(version_str[i], "skiboot",
strlen("skiboot")) == 0) {
/*
* If Skiboot was built with Buildroot its version may
* include a 'skiboot-' prefix; ignore it.
*/
if (strncmp(version, "skiboot-",
strlen("skiboot-")) == 0)
skiboot_version = version + strlen("skiboot-");
else
skiboot_version = version;
if (strncmp(prop, skiboot_version,
strlen(skiboot_version)) != 0)
prlog(PR_WARNING, "WARNING! Skiboot version does not match VERSION partition!\n");
}
}
}

void flash_dt_add_fw_version(void)
{
uint8_t version_data[80];
int rc;
int numbytes = 0, i = 0;
struct dt_node *fw_version;

if (version_buf == NULL)
return;

rc = wait_for_resource_loaded(RESOURCE_ID_VERSION, RESOURCE_SUBID_NONE);
if (rc != OPAL_SUCCESS) {
prlog(PR_WARNING, "FLASH: Failed to load VERSION data\n");
free(version_buf);
return;
}

fw_version = dt_new(dt_root, "ibm,firmware-versions");
assert(fw_version);

if (stb_is_container(version_buf, version_buf_size))
numbytes += SECURE_BOOT_HEADERS_SIZE;
for ( ; (numbytes < version_buf_size) && version_buf[numbytes]; numbytes++) {
if (version_buf[numbytes] == '\n') {
version_data[i] = '\0';
__flash_dt_add_fw_version(fw_version, version_data);
memset(version_data, 0, sizeof(version_data));
i = 0;
continue;
} else if (version_buf[numbytes] == '\t') {
continue; /* skip tabs */
}

version_data[i++] = version_buf[numbytes];
if (i == sizeof(version_data)) {
prlog(PR_WARNING, "VERSION item >%lu chars, skipping\n",
sizeof(version_data));
break;
}
}

free(version_buf);
}

void flash_fw_version_preload(void)
{
int rc;

if (proc_gen < proc_gen_p9)
return;

prlog(PR_INFO, "FLASH: Loading VERSION section\n");

version_buf = malloc(version_buf_size);
if (!version_buf) {
prlog(PR_WARNING, "FLASH: Failed to allocate memory\n");
return;
}

rc = start_preload_resource(RESOURCE_ID_VERSION, RESOURCE_SUBID_NONE,
version_buf, &version_buf_size);
if (rc != OPAL_SUCCESS) {
prlog(PR_WARNING,
"FLASH: Failed to start loading VERSION data\n");
free(version_buf);
version_buf = NULL;
}
}
147 changes: 0 additions & 147 deletions core/flash.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,6 @@ static struct lock flash_lock;
static struct flash *nvram_flash;
static u32 nvram_offset, nvram_size;

/* ibm,firmware-versions support */
static char *version_buf;
static size_t version_buf_size = 0x2000;

bool flash_reserve(void)
{
bool rc = false;
Expand Down Expand Up @@ -165,149 +161,6 @@ static int flash_nvram_write(uint32_t dst, void *src, uint32_t len)
return rc;
}

static void __flash_dt_add_fw_version(struct dt_node *fw_version, char* data)
{
static bool first = true;
char *prop;
int version_len, i;
int len = strlen(data);
const char *skiboot_version;
const char * version_str[] = {"open-power", "buildroot", "skiboot",
"hostboot-binaries", "hostboot", "linux",
"petitboot", "occ", "capp-ucode", "sbe",
"machine-xml", "hcode"};

if (first) {
first = false;

/* Increment past "key-" */
if (memcmp(data, "open-power", strlen("open-power")) == 0)
prop = data + strlen("open-power");
else
prop = strchr(data, '-');
if (!prop) {
prlog(PR_DEBUG,
"FLASH: Invalid fw version format (%s)\n", data);
return;
}
prop++;

dt_add_property_string(fw_version, "version", prop);
return;
}

/*
* PNOR version strings are not easily consumable. Split them into
* property, value.
*
* Example input from PNOR :
* "open-power-firestone-v1.8"
* "linux-4.4.6-openpower1-8420e0f"
*
* Desired output in device tree:
* open-power = "firestone-v1.8";
* linux = "4.4.6-openpower1-8420e0f";
*/
for(i = 0; i < ARRAY_SIZE(version_str); i++)
{
version_len = strlen(version_str[i]);
if (len < version_len)
continue;

if (memcmp(data, version_str[i], version_len) != 0)
continue;

/* Found a match, add property */
if (dt_find_property(fw_version, version_str[i]))
continue;

/* Increment past "key-" */
prop = data + version_len + 1;
dt_add_property_string(fw_version, version_str[i], prop);

/* Sanity check against what Skiboot thinks its version is. */
if (strncmp(version_str[i], "skiboot",
strlen("skiboot")) == 0) {
/*
* If Skiboot was built with Buildroot its version may
* include a 'skiboot-' prefix; ignore it.
*/
if (strncmp(version, "skiboot-",
strlen("skiboot-")) == 0)
skiboot_version = version + strlen("skiboot-");
else
skiboot_version = version;
if (strncmp(prop, skiboot_version,
strlen(skiboot_version)) != 0)
prlog(PR_WARNING, "WARNING! Skiboot version does not match VERSION partition!\n");
}
}
}

void flash_dt_add_fw_version(void)
{
uint8_t version_data[80];
int rc;
int numbytes = 0, i = 0;
struct dt_node *fw_version;

if (version_buf == NULL)
return;

rc = wait_for_resource_loaded(RESOURCE_ID_VERSION, RESOURCE_SUBID_NONE);
if (rc != OPAL_SUCCESS) {
prlog(PR_WARNING, "FLASH: Failed to load VERSION data\n");
free(version_buf);
return;
}

fw_version = dt_new(dt_root, "ibm,firmware-versions");
assert(fw_version);

if (stb_is_container(version_buf, version_buf_size))
numbytes += SECURE_BOOT_HEADERS_SIZE;
for ( ; (numbytes < version_buf_size) && version_buf[numbytes]; numbytes++) {
if (version_buf[numbytes] == '\n') {
version_data[i] = '\0';
__flash_dt_add_fw_version(fw_version, version_data);
memset(version_data, 0, sizeof(version_data));
i = 0;
continue;
} else if (version_buf[numbytes] == '\t') {
continue; /* skip tabs */
}

version_data[i++] = version_buf[numbytes];
}

free(version_buf);
}

void flash_fw_version_preload(void)
{
int rc;

if (proc_gen < proc_gen_p9)
return;

prlog(PR_INFO, "FLASH: Loading VERSION section\n");

version_buf = malloc(version_buf_size);
if (!version_buf) {
prlog(PR_WARNING, "FLASH: Failed to allocate memory\n");
return;
}

rc = start_preload_resource(RESOURCE_ID_VERSION, RESOURCE_SUBID_NONE,
version_buf, &version_buf_size);
if (rc != OPAL_SUCCESS) {
prlog(PR_WARNING,
"FLASH: Failed to start loading VERSION data\n");
free(version_buf);
version_buf = NULL;
}
}

static int flash_nvram_probe(struct flash *flash, struct ffs_handle *ffs)
{
uint32_t start, size, part;
Expand Down
18 changes: 18 additions & 0 deletions core/test/Makefile.check
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ CORE_TEST := \
core/test/run-bitmap \
core/test/run-device \
core/test/run-flash-subpartition \
core/test/run-flash-firmware-versions \
core/test/run-mem_region \
core/test/run-malloc \
core/test/run-malloc-speed \
Expand Down Expand Up @@ -72,6 +73,23 @@ $(CORE_TEST:%=%-gcov): %-gcov : %.c %
$(CORE_TEST_NOSTUB:%=%-gcov) : %-gcov : %.c %
$(call Q, HOSTCC ,$(HOSTCC) $(HOSTCFLAGS) $(HOSTGCOVCFLAGS) -I include -I . -I libfdt -lgcov -o $@ $< , $<)

core/test/run-flash-firmware-versions-gcov-run: core/test/run-flash-firmware-versions-inputs-gcov-run

core/test/run-flash-firmware-versions-inputs-gcov-run: core/test/run-flash-firmware-versions-gcov
$(call Q, TEST-COVERAGE , ./core/test/run-flash-firmware-versions-gcov core/test/firmware-versions-input/version-0 > /dev/null, $< version-0)
$(call Q, TEST-COVERAGE , ./core/test/run-flash-firmware-versions-gcov core/test/firmware-versions-input/version-1 > /dev/null, $< version-1)
$(call Q, TEST-COVERAGE , ./core/test/run-flash-firmware-versions-gcov core/test/firmware-versions-input/version-2 > /dev/null, $< version-2)
$(call Q, TEST-COVERAGE , ./core/test/run-flash-firmware-versions-gcov core/test/firmware-versions-input/version-10 > /dev/null, $< version-10)
$(call Q, TEST-COVERAGE , ./core/test/run-flash-firmware-versions-gcov core/test/firmware-versions-input/version-11 > /dev/null, $< version-11)
$(call Q, TEST-COVERAGE , ./core/test/run-flash-firmware-versions-gcov core/test/firmware-versions-input/version-16 > /dev/null, $< version-16)
$(call Q, TEST-COVERAGE , ./core/test/run-flash-firmware-versions-gcov core/test/firmware-versions-input/version-26 > /dev/null, $< version-26)
$(call Q, TEST-COVERAGE , ./core/test/run-flash-firmware-versions-gcov core/test/firmware-versions-input/version-27 > /dev/null, $< version-27)
$(call Q, TEST-COVERAGE , ./core/test/run-flash-firmware-versions-gcov core/test/firmware-versions-input/version-29 > /dev/null, $< version-29)
$(call Q, TEST-COVERAGE , ./core/test/run-flash-firmware-versions-gcov core/test/firmware-versions-input/version-trunc > /dev/null, $< version-trunc)
$(call Q, TEST-COVERAGE , ./core/test/run-flash-firmware-versions-gcov core/test/firmware-versions-input/version-long > /dev/null, $< version-long)
$(call Q, TEST-COVERAGE , ./core/test/run-flash-firmware-versions-gcov core/test/firmware-versions-input/version-nodash > /dev/null, $< version-nodash)


-include $(wildcard core/test/*.d)

clean: core-test-clean
Expand Down
Binary file added core/test/firmware-versions-input/version-0
Binary file not shown.
Binary file added core/test/firmware-versions-input/version-1
Binary file not shown.
Binary file added core/test/firmware-versions-input/version-10
Binary file not shown.
Binary file added core/test/firmware-versions-input/version-11
Binary file not shown.
Binary file added core/test/firmware-versions-input/version-16
Binary file not shown.
Binary file added core/test/firmware-versions-input/version-2
Binary file not shown.
Binary file added core/test/firmware-versions-input/version-26
Binary file not shown.
Binary file added core/test/firmware-versions-input/version-27
Binary file not shown.
Binary file added core/test/firmware-versions-input/version-29
Binary file not shown.
2 changes: 2 additions & 0 deletions core/test/firmware-versions-input/version-long
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
open-power-whatever-v2.0-10-g1cec21d-dirty
Well, I wonder what a short essay here will mean for parsing everything. I hope it is all okay, but we want to get greater than 80 chars.
2 changes: 2 additions & 0 deletions core/test/firmware-versions-input/version-nodash
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
no_dashes_in_version
this_is_wrong
2 changes: 2 additions & 0 deletions core/test/firmware-versions-input/version-trunc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
open-power-SUPERMICRO-P8DTU-V2.00.GA2-20161028
op

0 comments on commit 3170270

Please sign in to comment.