[PATCH 2/3] uefirtvariable: Test GetNextVariableName() returns unique variables
IvanHu
ivan.hu at canonical.com
Wed Mar 6 03:08:58 UTC 2013
On 03/06/2013 05:54 AM, Matt Fleming wrote:
> From: Matt Fleming <matt.fleming at intel.com>
>
> There have been reports that some implementations of
> GetNextVariableName() return duplicate variables,
>
> https://bugzilla.kernel.org/show_bug.cgi?id=47631
>
> Use a simple hash bucket algorithm to check the uniqueness of each
> variable and error if we encounter any duplicates.
>
> Signed-off-by: Matt Fleming <matt.fleming at intel.com>
> ---
> src/uefi/uefirtvariable/uefirtvariable.c | 155 +++++++++++++++++++++++++++++++
> 1 file changed, 155 insertions(+)
>
> diff --git a/src/uefi/uefirtvariable/uefirtvariable.c b/src/uefi/uefirtvariable/uefirtvariable.c
> index 177dbf9..fe4cdce 100644
> --- a/src/uefi/uefirtvariable/uefirtvariable.c
> +++ b/src/uefi/uefirtvariable/uefirtvariable.c
> @@ -401,7 +401,158 @@ static int getnextvariable_test2(fwts_framework *fw)
> return FWTS_OK;
>
> err:
> + return FWTS_ERROR;
> +}
> +
> +struct efi_var_item {
> + struct efi_var_item *next; /* collision chain */
> + uint16_t *name;
> + EFI_GUID *guid;
> + uint64_t hash;
> +};
> +
> +/*
> + * This is a completely arbitrary value.
> + */
> +#define BUCKET_SIZE 32
> +static struct efi_var_item *buckets[BUCKET_SIZE];
> +
> +/*
> + * This doesn't have to be a super efficient hashing function with
> + * minimal collisions, just more efficient than iterating over a
> + * simple linked list.
> + */
> +static inline uint64_t hash_func(uint16_t *variablename, uint64_t length)
> +{
> + uint64_t i, hash = 0;
> + uint16_t *c = variablename;
> +
> + for (i = 0; i < length; i++)
> + hash = hash * 33 + *c++;
> +
> + return hash;
> +}
> +
> +/*
> + * Return's 0 on success, -1 if there was a collision - meaning we've
> + * encountered duplicate variable names with the same GUID.
> + */
> +static int bucket_insert(struct efi_var_item *item)
> +{
> + struct efi_var_item *chain;
> + unsigned int index = item->hash % BUCKET_SIZE;
> +
> + chain = buckets[index];
> +
> + while (chain) {
> + /*
> + * OK, we got a collision - no big deal. Walk the
> + * chain and see whether this variable name and
> + * variable guid already appear on it.
> + */
> + if (compare_name(item->name, chain->name)) {
> + if (compare_guid(item->guid, chain->guid))
> + return -1; /* duplicate */
> + }
> +
> + chain = chain->next;
> + }
> +
> + item->next = buckets[index];
> + buckets[index] = item;
> + return 0;
> +}
> +
> +static void bucket_destroy(void)
> +{
> + struct efi_var_item *item;
> + int i;
> +
> + for (i = 0; i < BUCKET_SIZE; i++) {
> + item = buckets[i];
> +
> + while (item) {
> + struct efi_var_item *chain = item->next;
> +
> + free(item->name);
> + free(item);
> + item = chain;
> + }
> + }
> +}
> +
> +static int getnextvariable_test3(fwts_framework *fw)
> +{
> + long ioret;
> + uint64_t status;
>
> + struct efi_getnextvariablename getnextvariablename;
> + uint64_t variablenamesize = MAX_DATA_LENGTH;
> + uint16_t variablename[MAX_DATA_LENGTH];
> + EFI_GUID vendorguid;
> +
> + getnextvariablename.VariableNameSize = &variablenamesize;
> + getnextvariablename.VariableName = variablename;
> + getnextvariablename.VendorGuid = &vendorguid;
> + getnextvariablename.status = &status;
> +
> + /* To start the search, need to pass a Null-terminated string in VariableName */
> + variablename[0] = '\0';
> + while (true) {
> + struct efi_var_item *item;
> +
> + variablenamesize = MAX_DATA_LENGTH;
> + ioret = ioctl(fd, EFI_RUNTIME_GET_NEXTVARIABLENAME, &getnextvariablename);
> +
> + if (ioret == -1) {
> +
> + /* no next variable was found*/
> + if (*getnextvariablename.status == EFI_NOT_FOUND)
> + break;
> +
> + fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeGetNextVariableName",
> + "Failed to get next variable name with UEFI runtime service.");
> + fwts_uefi_print_status_info(fw, status);
> + goto err;
> + }
> +
> + item = malloc(sizeof(*item));
> + if (!item) {
> + fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeGetNextVariableName",
> + "Failed to allocate memory for test.");
> + goto err;
> + }
> +
> + item->guid = &vendorguid;
> + item->next = NULL;
> +
> + item->name = malloc(variablenamesize);
> + if (!item->name) {
> + fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeGetNextVariableName",
> + "Failed to allocate memory for test.");
> + free(item);
> + goto err;
> + }
> +
> + memcpy(item->name, variablename, variablenamesize);
> +
> + /* Ensure there are no duplicate variable names + guid */
> + item->hash = hash_func(variablename, variablenamesize);
> +
> + if (bucket_insert(item)) {
> + fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeGetNextVariableName",
> + "Duplicate variable name found.");
> + free(item->name);
> + free(item);
> + goto err;
> + }
> + };
> +
> + bucket_destroy();
> + return FWTS_OK;
> +
> +err:
> + bucket_destroy();
> return FWTS_ERROR;
> }
>
> @@ -931,6 +1082,10 @@ static int uefirtvariable_test2(fwts_framework *fw)
> if (ret != FWTS_OK)
> return ret;
>
> + ret = getnextvariable_test3(fw);
> + if (ret != FWTS_OK)
> + return ret;
> +
> fwts_passed(fw, "UEFI runtime service GetNextVariableName interface test passed.");
>
> return FWTS_OK;
>
Acked-by: Ivan Hu <ivan.hu at canonical.com>
More information about the fwts-devel
mailing list