From b774a56aa971def41ed8ee316b6be10b4478ed34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Trojnara?= Date: Wed, 23 Feb 2022 22:35:40 +0100 Subject: [PATCH] Floyd's cycle-finding algorithm --- msi.c | 49 ++++++++++++++++++++++++++++++++----------------- msi.h | 4 ++-- osslsigncode.c | 2 +- 3 files changed, 35 insertions(+), 20 deletions(-) diff --git a/msi.c b/msi.c index 6e5e41f..2d1d6f9 100644 --- a/msi.c +++ b/msi.c @@ -15,7 +15,7 @@ #define MIN(a,b) ((a) < (b) ? a : b) -static int recurse_entry(MSI_FILE *msi, uint32_t entryID, MSI_DIRENT *parent, MSI_DIRENT *prev); +static int recurse_entry(MSI_FILE *msi, uint32_t entryID, MSI_DIRENT *parent); /* Get absolute address from sector and offset */ static const u_char *sector_offset_to_address(MSI_FILE *msi, uint32_t sector, uint32_t offset) @@ -433,9 +433,11 @@ MSI_FILE *msi_file_new(char *buffer, uint32_t len) } /* Recursively create a tree of MSI_DIRENT structures */ -int msi_dirent_new(MSI_FILE *msi, MSI_ENTRY *entry, MSI_DIRENT *parent, MSI_DIRENT *prev, MSI_DIRENT **ret) +int msi_dirent_new(MSI_FILE *msi, MSI_ENTRY *entry, MSI_DIRENT *parent, MSI_DIRENT **ret) { - MSI_DIRENT *d, *dirent; + MSI_DIRENT *dirent; + static int cnt; + static MSI_DIRENT *tortoise, *hare; if (!entry) { return 1; /* OK */ @@ -445,12 +447,12 @@ int msi_dirent_new(MSI_FILE *msi, MSI_ENTRY *entry, MSI_DIRENT *parent, MSI_DIRE return 0; /* FAILED */ } - /* detect loops in previously visited entries (parents, siblings) */ - for (d = prev; d; d = d->prev) { - if ((entry->leftSiblingID != NOSTREAM && d->entry->leftSiblingID == entry->leftSiblingID) - || (entry->rightSiblingID != NOSTREAM && d->entry->rightSiblingID == entry->rightSiblingID) - || (entry->childID != NOSTREAM && d->entry->childID == entry->childID)) { - printf("Entry loop at ID: 0x%08X\n", entry->childID); + /* detect cycles in previously visited entries (parents, siblings) */ + if (!ret) { /* initialized (non-root entry) */ + if ((entry->leftSiblingID != NOSTREAM && tortoise->entry->leftSiblingID == entry->leftSiblingID) + || (entry->rightSiblingID != NOSTREAM && tortoise->entry->rightSiblingID == entry->rightSiblingID) + || (entry->childID != NOSTREAM && tortoise->entry->childID == entry->childID)) { + printf("MSI_ENTRY cycle detected at level %d\n", cnt); return 0; /* FAILED */ } } @@ -461,7 +463,19 @@ int msi_dirent_new(MSI_FILE *msi, MSI_ENTRY *entry, MSI_DIRENT *parent, MSI_DIRE dirent->type = entry->type; dirent->entry = entry; dirent->children = sk_MSI_DIRENT_new_null(); - dirent->prev = prev; + dirent->next = NULL; /* fail-safe */ + + /* Floyd's cycle-finding algorithm */ + if (!ret) { /* initialized (non-root entry) */ + if (cnt++ & 1) /* move the tortoise every other invocation of msi_dirent_new() */ + tortoise = tortoise->next; + hare->next = dirent; /* build a linked list of visited entries */ + hare = dirent; /* move the hare every time */ + } else { /* initialization needed (root entry) */ + cnt = 0; + tortoise = dirent; + hare = dirent; + } if (parent && !sk_MSI_DIRENT_push(parent->children, dirent)) { printf("Failed to insert MSI_DIRENT\n"); @@ -470,9 +484,9 @@ int msi_dirent_new(MSI_FILE *msi, MSI_ENTRY *entry, MSI_DIRENT *parent, MSI_DIRE return 0; /* FAILED */ } - if (!recurse_entry(msi, entry->leftSiblingID, parent, dirent) - || !recurse_entry(msi, entry->rightSiblingID, parent, dirent) - || (entry->type != DIR_STREAM && !recurse_entry(msi, entry->childID, dirent, dirent))) { + if (!recurse_entry(msi, entry->leftSiblingID, parent) + || !recurse_entry(msi, entry->rightSiblingID, parent) + || !recurse_entry(msi, entry->childID, dirent)) { printf("Failed to add a sibling or a child to the tree\n"); sk_MSI_DIRENT_free(dirent->children); OPENSSL_free(dirent); @@ -486,11 +500,12 @@ int msi_dirent_new(MSI_FILE *msi, MSI_ENTRY *entry, MSI_DIRENT *parent, MSI_DIRE } /* Add a sibling or a child to the tree */ -static int recurse_entry(MSI_FILE *msi, uint32_t entryID, MSI_DIRENT *parent, MSI_DIRENT *dirent) { - /* NOTE : These links are a tree, not a linked list */ +/* NOTE: These links are a tree, not a linked list */ +static int recurse_entry(MSI_FILE *msi, uint32_t entryID, MSI_DIRENT *parent) +{ MSI_ENTRY *node; - /* The special value NOSTREAM (0xFFFFFFFF) is used as a terminator */ + /* The special NOSTREAM (0xFFFFFFFF) value is used as a terminator */ if (entryID == NOSTREAM) /* stop condition */ return 1; /* OK */ @@ -500,7 +515,7 @@ static int recurse_entry(MSI_FILE *msi, uint32_t entryID, MSI_DIRENT *parent, MS return 0; /* FAILED */ } - if (!msi_dirent_new(msi, node, parent, dirent, NULL)) { + if (!msi_dirent_new(msi, node, parent, NULL)) { OPENSSL_free(node); return 0; /* FAILED */ } diff --git a/msi.h b/msi.h index cf94585..266ed07 100644 --- a/msi.h +++ b/msi.h @@ -145,7 +145,7 @@ typedef struct msi_dirent_struct { uint8_t type; MSI_ENTRY *entry; STACK_OF(MSI_DIRENT) *children; - struct msi_dirent_struct *prev; /* detect loops */ + struct msi_dirent_struct *next; /* for cycle detection */ } MSI_DIRENT; DEFINE_STACK_OF(MSI_DIRENT) @@ -204,7 +204,7 @@ int msi_file_read(MSI_FILE *msi, MSI_ENTRY *entry, uint32_t offset, char *buffer MSI_FILE *msi_file_new(char *buffer, uint32_t len); void msi_file_free(MSI_FILE *msi); MSI_ENTRY *msi_root_entry_get(MSI_FILE *msi); -int msi_dirent_new(MSI_FILE *msi, MSI_ENTRY *entry, MSI_DIRENT *parent, MSI_DIRENT *prev, MSI_DIRENT **ret); +int msi_dirent_new(MSI_FILE *msi, MSI_ENTRY *entry, MSI_DIRENT *parent, MSI_DIRENT **ret); MSI_ENTRY *msi_signatures_get(MSI_DIRENT *dirent, MSI_ENTRY **dse); void msi_dirent_free(MSI_DIRENT *dirent); int msi_prehash_dir(MSI_DIRENT *dirent, BIO *hash, int is_root); diff --git a/osslsigncode.c b/osslsigncode.c index 0b61538..bc9a9b6 100644 --- a/osslsigncode.c +++ b/osslsigncode.c @@ -2919,7 +2919,7 @@ static int msi_verify_header(char *indata, uint32_t filesize, MSI_PARAMS *msipar printf("Failed to get file entry\n"); return 0; /* FAILED */ } - if (!msi_dirent_new(msiparams->msi, root, NULL, NULL, &root_dir)) { + if (!msi_dirent_new(msiparams->msi, root, NULL, &root_dir)) { printf("Failed to parse MSI_DIRENT struct\n"); OPENSSL_free(root); return 0; /* FAILED */