Instructions:
Read through the `giftcardreader.c` program (and its accompanying header, `giftcard.h`) to get a feel for it. You should also try building and running it with the included `examplefile.gft` file, to see what its normal output is. For this part, your job will be to find some flaws in the program, and
then create test cases (i.e., binary gift cards) that expose flaws in the program. You should write:
1. *Two* test cases, `crash1.gft` and `crash2.gft`, that cause the
program to crash; each crash should have a different root cause.
2. One test case, `hang.gft`, that causes the program to loop
infinitely. (Hint: you may want to examine the "animation" record type
to find a bug that causes the program to loop infinitely.)
3. A text file, `part2.txt` explaining the bug triggered by each of your
three test cases.
To create your own test files, you may want to refer to the `gengift.py` and `genanim.py` programs, which are Python scripts that create giftcard files of different types. Finally, fix the bugs that are triggered by your test cases, and verify that the program no longer crashes / hangs on your test cases. To make sure that these bugs don't come up again as the code evolves, have Github Actions automatically build and run the program on your test suit
giftcard.h #include struct this_gift_card { int num_bytes; void *gift_card_data; }; struct gift_card_data { char *merchant_id; // always points to 32 bytes of data char *customer_id;// always points to 32 bytes of data int number_of_gift_card_records; void **gift_card_record_data; // many can exist here. }; struct gift_card_record_data { int record_size_in_bytes; // size of this entry, including this field. int type_of_record; // 1 for amount change, 2 for message void *actual_record; // polymorphism }; struct gift_card_amount_change { int amount_added; // always 32 bytes. Only appears if amount_added is positive char *actual_signature; }; // BDG: I'm not sure these sizes are right... might want to double-check // what the actual program does. struct gift_card_program { char *message; // 32 bytes unsigned char *program; // 128 bytes }; giftcard.c /* * Gift Card Reading Application * Original Author: Shoddycorp's Cut-Rate Contracting * Comments added by: Justin Cappos (JAC) and Brendan Dolan-Gavitt (BDG) * Maintainer: * Date: 8 July 2020 */ #include "giftcard.h" #include #include #include // .,~==== interpreter for THX-1138 assembly ====~,. // // This is an emulated version of a microcontroller with // 16 registers, one flag (the zero flag), and display // functionality. Programs can operate on the message // buffer and use opcode 0x07 to update the display, so // that animated greetings can be created. void animate(char *msg, unsigned char *program) { unsigned char regs[16]; char *mptr = msg; // TODO: how big is this buffer? unsigned char *pc = program; int i = 0; int zf = 0; while (pc < program+256) { unsigned char op, arg1, arg2; op = *pc; arg1 = *(pc+1); arg2 = *(pc+2); switch (*pc) { case 0x00: break; case 0x01: regs[arg1] = *mptr; break; case 0x02: *mptr = regs[arg1]; break; case 0x03: mptr += (char)arg1; break; case 0x04: regs[arg2] = arg1; break; case 0x05: regs[arg1] ^= regs[arg2]; zf = !regs[arg1]; break; case 0x06: regs[arg1] += regs[arg2]; zf = !regs[arg1]; break; case 0x07: puts(msg); break; case 0x08: goto done; case 0x09: pc += (char)arg1; break; case 0x10: if (zf) pc += (char)arg1; break; } pc+=3; #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION // Slow down animation to make it more visible (disabled if fuzzing) usleep(5000); #endif } done: return; } int get_gift_card_value(struct this_gift_card *thisone) { struct gift_card_data *gcd_ptr; struct gift_card_record_data *gcrd_ptr; struct gift_card_amount_change *gcac_ptr; int ret_count = 0; gcd_ptr = thisone->gift_card_data; for(int i=0;inumber_of_gift_card_records; i++) { gcrd_ptr = (struct gift_card_record_data *) gcd_ptr->gift_card_record_data[i]; if (gcrd_ptr->type_of_record == 1) { gcac_ptr = gcrd_ptr->actual_record; ret_count += gcac_ptr->amount_added; } } return ret_count; } void print_gift_card_info(struct this_gift_card *thisone) { struct gift_card_data *gcd_ptr; struct gift_card_record_data *gcrd_ptr; struct gift_card_amount_change *gcac_ptr; struct gift_card_program *gcp_ptr; gcd_ptr = thisone->gift_card_data; printf(" Merchant ID: %32.32s\n",gcd_ptr->merchant_id); printf(" Customer ID: %32.32s\n",gcd_ptr->customer_id); printf(" Num records: %d\n",gcd_ptr->number_of_gift_card_records); for(int i=0;inumber_of_gift_card_records; i++) { gcrd_ptr = (struct gift_card_record_data *) gcd_ptr->gift_card_record_data[i]; if (gcrd_ptr->type_of_record == 1) { printf(" record_type: amount_change\n"); gcac_ptr = gcrd_ptr->actual_record; printf(" amount_added: %d\n",gcac_ptr->amount_added); if (gcac_ptr->amount_added>0) { printf(" signature: %32.32s\n",gcac_ptr->actual_signature); } } else if (gcrd_ptr->type_of_record == 2) { printf(" record_type: message\n"); printf(" message: %s\n",(char *)gcrd_ptr->actual_record); } else if (gcrd_ptr->type_of_record == 3) { gcp_ptr = gcrd_ptr->actual_record; printf(" record_type: animated message\n"); // BDG: Hmm... is message guaranteed to be null-terminated? printf(" message: %s\n", gcp_ptr->message); printf(" [running embedded program] \n"); animate(gcp_ptr->message, gcp_ptr->program); } } printf(" Total value: %d\n\n",get_gift_card_value(thisone)); } // Added to support web functionalities void gift_card_json(struct this_gift_card *thisone) { struct gift_card_data *gcd_ptr; struct gift_card_record_data *gcrd_ptr; struct gift_card_amount_change *gcac_ptr; gcd_ptr = thisone->gift_card_data; printf("{\n"); printf(" \"merchant_id\": \"%32.32s\",\n", gcd_ptr->merchant_id); printf(" \"customer_id\": \"%32.32s\",\n", gcd_ptr->customer_id); printf(" \"total_value\": %d,\n", get_gift_card_value(thisone)); printf(" \"records\": [\n"); for(int i=0;inumber_of_gift_card_records; i++) { gcrd_ptr = (struct gift_card_record_data *) gcd_ptr->gift_card_record_data[i]; printf(" {\n"); if (gcrd_ptr->type_of_record == 1) { printf(" \"record_type\": \"amount_change\",\n"); gcac_ptr = gcrd_ptr->actual_record; printf(" \"amount_added\": %d,\n",gcac_ptr->amount_added); if (gcac_ptr->amount_added>0) { printf(" \"signature\": \"%32.32s\"\n",gcac_ptr->actual_signature); } } else if (gcrd_ptr->type_of_record == 2) { printf(" \"record_type\": \"message\",\n"); printf(" \"message\": \"%s\"\n",(char *)gcrd_ptr->actual_record); } else if (gcrd_ptr->type_of_record == 3) { struct gift_card_program *gcp = gcrd_ptr->actual_record; printf(" \"record_type\": \"animated message\",\n"); printf(" \"message\": \"%s\",\n",gcp->message); // programs are binary so we will hex for the json char *hexchars = "01234567890abcdef"; char program_hex[512+1]; program_hex[512] = '\0'; int i; for(i = 0; i < 256; i++) { program_hex[i*2] = hexchars[((gcp->program[i] & 0xf0) >> 4)]; program_hex[i*2+1] = hexchars[(gcp->program[i] & 0x0f)]; } printf(" \"program\": \"%s\"\n",program_hex); } if (i < gcd_ptr->number_of_gift_card_records-1) printf(" },\n"); else printf(" }\n"); } printf(" ]\n"); printf("}\n"); } struct this_gift_card *gift_card_reader(FILE *input_fd) { struct this_gift_card *ret_val = malloc(sizeof(struct this_gift_card)); void *optr; void *ptr; // Loop to do the whole file while (!feof(input_fd)) { struct gift_card_data *gcd_ptr; /* JAC: Why aren't return types checked? */ fread(&ret_val->num_bytes, 4,1, input_fd); // Make something the size of the rest and read it in if(ret_val->num_bytes < 0){ exit(1); } //this coditional statement fixed crash 1 ptr = malloc(ret_val->num_bytes); fread(ptr, ret_val->num_bytes, 1, input_fd); optr = ptr-4; gcd_ptr = ret_val->gift_card_data = malloc(sizeof(struct gift_card_data)); gcd_ptr->merchant_id = ptr; ptr += 32; // printf("VD: %d\n",(int)ptr - (int) gcd_ptr->merchant_id); gcd_ptr->customer_id = ptr; ptr += 32; /* JAC: Something seems off here... */ gcd_ptr->number_of_gift_card_records = *((char *)ptr); ptr += 4; gcd_ptr->gift_card_record_data = (void *)malloc(gcd_ptr->number_of_gift_card_records*sizeof(void*)); // Now ptr points at the gift card recrod data for (int i=0; i < gcd_ptr->number_of_gift_card_records; i++){ //printf("i: %d\n",i); struct gift_card_record_data *gcrd_ptr; gcrd_ptr = gcd_ptr->gift_card_record_data[i] = malloc(sizeof(struct gift_card_record_data)); struct gift_card_amount_change *gcac_ptr; gcac_ptr = gcrd_ptr->actual_record = malloc(sizeof(struct gift_card_record_data)); struct gift_card_program *gcp_ptr; gcp_ptr = malloc(sizeof(struct gift_card_program)); gcrd_ptr->record_size_in_bytes = *((char *)ptr); //printf("rec at %x, %d bytes\n", ptr - optr, gcrd_ptr->record_size_in_bytes); ptr += 4; //printf("record_data: %d\n",gcrd_ptr->record_size_in_bytes); gcrd_ptr->type_of_record = *((char *)ptr); ptr += 4; //printf("type of rec: %d\n", gcrd_ptr->type_of_record); // amount change if (gcrd_ptr->type_of_record == 1) { gcac_ptr->amount_added = *((int*) ptr); ptr += 4; // don't need a sig if negative /* JAC: something seems off here */ if (gcac_ptr < 0) break; gcac_ptr->actual_signature = ptr; ptr+=32; } // message if (gcrd_ptr->type_of_record == 2) { gcrd_ptr->actual_record = ptr; // advance by the string size + 1 for nul // BDG: does not seem right ptr=ptr+strlen((char *)gcrd_ptr->actual_record)+1; } // BDG: gift cards can run code?? Might want to check this one carefully... // text animatino (BETA) if (gcrd_ptr->type_of_record == 3) { gcp_ptr->message = malloc(32); gcp_ptr->program = malloc(256); memcpy(gcp_ptr->message, ptr, 32); ptr+=32; memcpy(gcp_ptr->program, ptr, 256); ptr+=256; gcrd_ptr->actual_record = gcp_ptr; } if (gcrd_ptr->type_of_record > 3) { printf("unknown record type: %d\n", gcrd_ptr->type_of_record); exit(1); } } } return ret_val; } struct this_gift_card *thisone; int main(int argc, char **argv) { if (argc != 3) { fprintf(stderr, "usage: %s <1|2> file.gft\n", argv[0]); fprintf(stderr, " - Use 1 for text output, 2 for JSON output\n"); return 1; } FILE *input_fd = fopen(argv[2],"r"); if (!input_fd) { fprintf(stderr, "error opening file\n"); return 1; } thisone = gift_card_reader(input_fd); if (argv[1][0] == '1') print_gift_card_info(thisone); else if (argv[1][0] == '2') gift_card_json(thisone); return 0; } gengift.py import struct import sys # We build the content of the file in a byte string first # This lets us calculate the length for the header at the end data = b'' data += b"GiftCardzzzzz.com".ljust(32, b' ') # Merchant ID data += b"B"*32 # Customer ID data += struct.pack("