Floyd's cycle-finding algorithm

This commit is contained in:
Michał Trojnara 2022-02-23 22:35:40 +01:00
parent 6eaf0d9368
commit b774a56aa9
3 changed files with 35 additions and 20 deletions

49
msi.c
View File

@ -15,7 +15,7 @@
#define MIN(a,b) ((a) < (b) ? a : b) #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 */ /* Get absolute address from sector and offset */
static const u_char *sector_offset_to_address(MSI_FILE *msi, uint32_t sector, uint32_t 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 */ /* 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) { if (!entry) {
return 1; /* OK */ 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 */ return 0; /* FAILED */
} }
/* detect loops in previously visited entries (parents, siblings) */ /* detect cycles in previously visited entries (parents, siblings) */
for (d = prev; d; d = d->prev) { if (!ret) { /* initialized (non-root entry) */
if ((entry->leftSiblingID != NOSTREAM && d->entry->leftSiblingID == entry->leftSiblingID) if ((entry->leftSiblingID != NOSTREAM && tortoise->entry->leftSiblingID == entry->leftSiblingID)
|| (entry->rightSiblingID != NOSTREAM && d->entry->rightSiblingID == entry->rightSiblingID) || (entry->rightSiblingID != NOSTREAM && tortoise->entry->rightSiblingID == entry->rightSiblingID)
|| (entry->childID != NOSTREAM && d->entry->childID == entry->childID)) { || (entry->childID != NOSTREAM && tortoise->entry->childID == entry->childID)) {
printf("Entry loop at ID: 0x%08X\n", entry->childID); printf("MSI_ENTRY cycle detected at level %d\n", cnt);
return 0; /* FAILED */ 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->type = entry->type;
dirent->entry = entry; dirent->entry = entry;
dirent->children = sk_MSI_DIRENT_new_null(); 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)) { if (parent && !sk_MSI_DIRENT_push(parent->children, dirent)) {
printf("Failed to insert MSI_DIRENT\n"); 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 */ return 0; /* FAILED */
} }
if (!recurse_entry(msi, entry->leftSiblingID, parent, dirent) if (!recurse_entry(msi, entry->leftSiblingID, parent)
|| !recurse_entry(msi, entry->rightSiblingID, parent, dirent) || !recurse_entry(msi, entry->rightSiblingID, parent)
|| (entry->type != DIR_STREAM && !recurse_entry(msi, entry->childID, dirent, dirent))) { || !recurse_entry(msi, entry->childID, dirent)) {
printf("Failed to add a sibling or a child to the tree\n"); printf("Failed to add a sibling or a child to the tree\n");
sk_MSI_DIRENT_free(dirent->children); sk_MSI_DIRENT_free(dirent->children);
OPENSSL_free(dirent); 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 */ /* 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; 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 */ if (entryID == NOSTREAM) /* stop condition */
return 1; /* OK */ return 1; /* OK */
@ -500,7 +515,7 @@ static int recurse_entry(MSI_FILE *msi, uint32_t entryID, MSI_DIRENT *parent, MS
return 0; /* FAILED */ return 0; /* FAILED */
} }
if (!msi_dirent_new(msi, node, parent, dirent, NULL)) { if (!msi_dirent_new(msi, node, parent, NULL)) {
OPENSSL_free(node); OPENSSL_free(node);
return 0; /* FAILED */ return 0; /* FAILED */
} }

4
msi.h
View File

@ -145,7 +145,7 @@ typedef struct msi_dirent_struct {
uint8_t type; uint8_t type;
MSI_ENTRY *entry; MSI_ENTRY *entry;
STACK_OF(MSI_DIRENT) *children; STACK_OF(MSI_DIRENT) *children;
struct msi_dirent_struct *prev; /* detect loops */ struct msi_dirent_struct *next; /* for cycle detection */
} MSI_DIRENT; } MSI_DIRENT;
DEFINE_STACK_OF(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); MSI_FILE *msi_file_new(char *buffer, uint32_t len);
void msi_file_free(MSI_FILE *msi); void msi_file_free(MSI_FILE *msi);
MSI_ENTRY *msi_root_entry_get(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); MSI_ENTRY *msi_signatures_get(MSI_DIRENT *dirent, MSI_ENTRY **dse);
void msi_dirent_free(MSI_DIRENT *dirent); void msi_dirent_free(MSI_DIRENT *dirent);
int msi_prehash_dir(MSI_DIRENT *dirent, BIO *hash, int is_root); int msi_prehash_dir(MSI_DIRENT *dirent, BIO *hash, int is_root);

View File

@ -2919,7 +2919,7 @@ static int msi_verify_header(char *indata, uint32_t filesize, MSI_PARAMS *msipar
printf("Failed to get file entry\n"); printf("Failed to get file entry\n");
return 0; /* FAILED */ 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"); printf("Failed to parse MSI_DIRENT struct\n");
OPENSSL_free(root); OPENSSL_free(root);
return 0; /* FAILED */ return 0; /* FAILED */