AmendHub

Download:

jcs

/

subtext

/

amendments

/

220

*: Use NewPtr instead of malloc, add malloc and free debugging

Instead of free(ptr), use xfree(&ptr) and after it calls
DisposePtr(ptr), it will update ptr to point at NULL to catch
use-after-frees.
 
When MALLOC_DEBUG is defined, each allocation is added to a
list, and removed when freed. If it's not in the list at free
time, it's a double-free. Also, instead of pointing pointers at
NULL, point them to a pre-defined block of zeroes and in the
idle loop, periodically check that this block is still zero. This
will also catch use-after-frees in a more reliable (but costly)
way.

jcs made amendment 220 over 2 years ago
--- bile.c Tue Jul 19 13:30:56 2022 +++ bile.c Wed Jul 20 09:30:20 2022 @@ -81,7 +81,7 @@ bile_create(const Str255 filename, short vrefnum, cons len = BILE_MAGIC_LEN; tmp = xstrdup(BILE_MAGIC); _bile_error = FSWrite(bile->frefnum, &len, tmp); - free(tmp); + xfree(&tmp); if (_bile_error) goto create_bail; @@ -103,7 +103,7 @@ bile_create(const Str255 filename, short vrefnum, cons _bile_error = FSWrite(bile->frefnum, &len, tmp); if (_bile_error) goto create_bail; - free(tmp); + xfree(&tmp); GetFPos(fh, &bile->file_size); @@ -117,7 +117,7 @@ create_bail: FSClose(bile->frefnum); bile->frefnum = -1; if (bile != NULL) - free(bile); + xfree(&bile); return NULL; } @@ -195,7 +195,7 @@ open_bail: FSClose(bile->frefnum); bile->frefnum = -1; if (bile != NULL) - free(bile); + xfree(&bile); return NULL; } @@ -262,7 +262,7 @@ bile_close(struct bile *bile) FSClose(bile->frefnum); bile->frefnum = -1; if (bile->map != NULL) - free(bile->map); + xfree(&bile->map); } struct bile_object * @@ -898,7 +898,7 @@ bile_read_map(struct bile *bile, struct bile_object *m map = xmalloczero(size); _bile_error = FSRead(bile->frefnum, &size, map); if (_bile_error) { - free(map); + xfree(&map); return -1; } @@ -975,7 +975,7 @@ bile_write_map(struct bile *bile) } /* successfully wrote new map, switch over */ - free(bile->map); + xfree(&bile->map); bile->nobjects = new_nobjects; bile->map = new_map; bile->old_map_ptr.pos = bile->map_ptr.pos; --- board.c Tue Jul 19 15:35:47 2022 +++ board.c Wed Jul 20 09:32:20 2022 @@ -166,7 +166,7 @@ board_show(struct session *s, short id) while (!done && !s->ending) { if (find_post_ids) { if (post_ids != NULL) { - free(post_ids); + xfree(&post_ids); post_ids = NULL; } nall_post_ids = board_find_post_ids(board, &npost_ids, @@ -261,7 +261,7 @@ handle_opt: } if (post_ids != NULL) - free(post_ids); + xfree(&post_ids); } void @@ -296,19 +296,19 @@ board_list_posts(struct session *s, struct board *boar &data); bile_unmarshall_object(board->bile, board_post_object_fields, nboard_post_object_fields, data, size, &post, false); - free(data); + xfree(&data); if (post.thread_id != thread.thread_id) { if (thread.thread_id) { - free(thread.subject); - free(thread.post_ids); - free(thread.parent_post_ids); + xfree(&thread.subject); + xfree(&thread.post_ids); + xfree(&thread.parent_post_ids); } size = bile_read_alloc(board->bile, BOARD_THREAD_RTYPE, post.thread_id, &data); bile_unmarshall_object(board->bile, board_thread_object_fields, nboard_thread_object_fields, data, size, &thread, true); - free(data); + xfree(&data); for (j = 0; j < nitems(indent_parent_ids); j++) indent_parent_ids[j] = 0; @@ -358,11 +358,11 @@ board_list_posts(struct session *s, struct board *boar session_flush(s); if (thread.subject != NULL) - free(thread.subject); + xfree(&thread.subject); if (thread.post_ids != NULL) - free(thread.post_ids); + xfree(&thread.post_ids); if (thread.parent_post_ids != NULL) - free(thread.parent_post_ids); + xfree(&thread.parent_post_ids); } unsigned long @@ -413,7 +413,7 @@ post_compose_start: tmp = session_field_input(s, 100, 50, thread->subject, false, 0); if (thread->subject != NULL) - free(thread->subject); + xfree(&thread->subject); thread->subject = tmp; session_output(s, "\r\n", 2); session_flush(s); @@ -427,7 +427,7 @@ post_compose_start: session_printf(s, "{{B}}Error:{{/B}} Subject " "cannot be blank (^C to cancel)\r\n"); session_flush(s); - free(thread->subject); + xfree(&thread->subject); continue; } thread->subject_size = strlen(thread->subject) + 1; @@ -442,7 +442,7 @@ post_compose_start: tmp = session_field_input(s, 2048, s->terminal_columns - 1, post.body, true, 0); if (post.body != NULL) - free(post.body); + xfree(&post.body); post.body = tmp; session_output(s, "\r\n", 2); session_flush(s); @@ -453,7 +453,7 @@ post_compose_start: rtrim(post.body, "\r\n\t "); if (post.body[0] == '\0') { - free(post.body); + xfree(&post.body); goto post_compose_done; } post.body_size = strlen(post.body) + 1; @@ -514,11 +514,11 @@ post_compose_error: post_compose_done: if (parent_post == NULL) { if (thread->subject != NULL) - free(thread->subject); - free(thread); + xfree(&thread->subject); + xfree(&thread); } if (post.body) - free(post.body); + xfree(&post.body); return post.id; } @@ -554,7 +554,7 @@ board_post_read(struct session *s, struct board *board bile_error(board->bile)); bile_unmarshall_object(board->bile, board_post_object_fields, nboard_post_object_fields, data, size, &post, true); - free(data); + xfree(&data); size = bile_read_alloc(board->bile, BOARD_THREAD_RTYPE, post.thread_id, &data); @@ -563,7 +563,7 @@ board_post_read(struct session *s, struct board *board bile_error(board->bile)); bile_unmarshall_object(board->bile, board_thread_object_fields, nboard_thread_object_fields, data, size, &thread, true); - free(data); + xfree(&data); dopts = xmalloc(sizeof(opts)); memcpy(dopts, opts, sizeof(opts)); @@ -694,8 +694,8 @@ board_find_post_ids(struct board *board, size_t *npost bile_read_alloc(board->bile, BOARD_THREAD_RTYPE, o->id, &data); bile_unmarshall_object(board->bile, board_thread_object_fields, nboard_thread_object_fields, data, o->size, &thread, false); - free(data); - free(o); + xfree(&data); + xfree(&o); thread_map[n].id = thread.thread_id; thread_map[n].time = thread.last_post_at; @@ -724,7 +724,7 @@ board_find_post_ids(struct board *board, size_t *npost thread_map[j].id, &data); bile_unmarshall_object(board->bile, board_thread_object_fields, nboard_thread_object_fields, data, size, &thread, true); - free(data); + xfree(&data); for (i = 0; i < thread.nposts; i++) { if (offset > 0 && seen_posts++ < offset) @@ -740,8 +740,8 @@ board_find_post_ids(struct board *board, size_t *npost break; } - free(thread.post_ids); - free(thread.subject); + xfree(&thread.post_ids); + xfree(&thread.subject); if (*npost_ids >= limit) break; @@ -749,7 +749,7 @@ board_find_post_ids(struct board *board, size_t *npost done: if (thread_map != NULL) - free(thread_map); + xfree(&thread_map); return nall_post_ids; } @@ -783,7 +783,7 @@ board_post_create(struct board *board, struct board_th size) != size) { warn("bile_write of new post failed! %d", bile_error(board->bile)); post->id = 0; - free(data); + xfree(&data); goto done; } @@ -829,7 +829,7 @@ board_post_create(struct board *board, struct board_th post->id = 0; goto done; } - free(data); + xfree(&data); bile_flush(board->bile, true); @@ -886,9 +886,9 @@ board_delete_post(struct session *s, struct board *boa thread->nposts--; - free(thread->post_ids); + xfree(&thread->post_ids); thread->post_ids = new_post_ids; - free(thread->parent_post_ids); + xfree(&thread->parent_post_ids); thread->parent_post_ids = new_parent_post_ids; ret = bile_marshall_object(board->bile, board_thread_object_fields, @@ -901,10 +901,10 @@ board_delete_post(struct session *s, struct board *boa data, size) != size) { warn("bile_write of updated thread after post delete failed! " "%d", bile_error(board->bile)); - free(data); + xfree(&data); return; } - free(data); + xfree(&data); bile_delete(board->bile, BOARD_POST_RTYPE, post->id); @@ -914,7 +914,7 @@ board_delete_post(struct session *s, struct board *boa /* all we can do is change the post to say deleted */ if (post->body != NULL) - free(post->body); + xfree(&post->body); snprintf(del, sizeof(del), "[ Post deleted by %s ]", s->user ? s->user->username : "unknown"); post->body = xstrdup(del); @@ -930,7 +930,7 @@ board_delete_post(struct session *s, struct board *boa size) != size) { warn("bile_write of updated post failed! %d", bile_error(board->bile)); - free(data); + xfree(&data); return; } } --- chat.c Sat Jul 16 14:50:15 2022 +++ chat.c Wed Jul 20 09:32:36 2022 @@ -198,7 +198,7 @@ chat_start(struct session *s, char *with_node) if (strcmp(input, "/quit") == 0 || strcmp(input, "/exit") == 0 || strcmp(input, "/leave") == 0) { - free(input); + xfree(&input); break; } else if (strcmp(input, "/help") == 0) { chat_help(s); @@ -220,7 +220,7 @@ chat_start(struct session *s, char *with_node) session_flush(s); } - free(input); + xfree(&input); } session_log(s, "Left chat with %s", with_node[0] ? with_node : --- console.c Sat Jul 16 23:05:59 2022 +++ console.c Wed Jul 20 12:56:21 2022 @@ -72,7 +72,7 @@ console_init(void) console->session = session_create("console", "console", &console_node_funcs); if (console->session == NULL) { - free(console); + xfree(&console); warn("No free nodes for a console"); return NULL; } @@ -118,7 +118,7 @@ console_init(void) if ((sysop_username = user_first_sysop_username()) != NULL) { strlcpy(console->session->autologin_username, sysop_username, sizeof(console->session->autologin_username)); - free(sysop_username); + xfree(&sysop_username); } return console; @@ -337,7 +337,7 @@ console_close_from_session(struct session *session) struct console *console = (struct console *)session->cookie; session_log(session, "Closing console session"); destroy_focusable(console->focusable); - free(console); + xfree(&console); } short --- db.c Wed Jul 13 17:45:43 2022 +++ db.c Wed Jul 20 09:34:13 2022 @@ -202,7 +202,7 @@ db_init(Str255 path, short vrefnum, struct bile *bile) if (db_migrate(tdb, was_new) != 0) { bile_close(tdb->bile); - free(tdb); + xfree(&tdb); return NULL; } @@ -232,12 +232,12 @@ void db_close(struct db *tdb) { bile_close(tdb->bile); - free(tdb->bile); + xfree(&tdb->bile); if (tdb->sessions_bile != NULL) { bile_close(tdb->sessions_bile); - free(tdb->sessions_bile); + xfree(&tdb->sessions_bile); } - free(tdb); + xfree(&tdb); } short @@ -309,7 +309,7 @@ db_migrate(struct db *tdb, short is_new) user.is_enabled = DB_TRUE; bile_write(tdb->bile, DB_USER_RTYPE, o->id, (char *)&user, sizeof(user)); - free(o); + xfree(&o); nuser++; } break; @@ -424,7 +424,7 @@ db_config_load(struct db *tdb) rlen, sizeof(tdb->config)); memcpy(&tdb->config, newconfig, sizeof(tdb->config)); - free(newconfig); + xfree(&newconfig); } void @@ -442,7 +442,7 @@ db_cache_boards(struct db *tdb) if (tdb->boards[n].bile) bile_close(tdb->boards[n].bile); } - free(tdb->boards); + xfree(&tdb->boards); } if (getpath(tdb->bile->vrefnum, tdb->bile->filename, &db_filename, @@ -470,10 +470,10 @@ db_cache_boards(struct db *tdb) bile_unmarshall_object(tdb->bile, board_object_fields, nboard_object_fields, data, size, (char *)(&tdb->boards[n]), true); - free(data); + xfree(&data); } - free(ids); + xfree(&ids); for (n = 0; n < tdb->nboards; n++) { snprintf((char *)board_filename, sizeof(board_filename), @@ -527,7 +527,7 @@ db_board_create(struct db *tdb, struct board *board) if (bile_write(tdb->bile, DB_BOARD_RTYPE, board->id, data, size) != size) panic("save of new board failed: %d", bile_error(tdb->bile)); - free(data); + xfree(&data); if (getpath(tdb->bile->vrefnum, tdb->bile->filename, &db_filename, false) != 0) @@ -561,7 +561,7 @@ db_cache_folders(struct db *tdb) if (tdb->folders[n].bile) bile_close(tdb->folders[n].bile); } - free(tdb->folders); + xfree(&tdb->folders); } if (getpath(tdb->bile->vrefnum, tdb->bile->filename, &db_filename, @@ -588,10 +588,10 @@ db_cache_folders(struct db *tdb) bile_unmarshall_object(tdb->bile, folder_object_fields, nfolder_object_fields, data, size, (char *)(&tdb->folders[n]), true); - free(data); + xfree(&data); } - free(ids); + xfree(&ids); for (n = 0; n < tdb->nfolders; n++) { snprintf((char *)folder_filename, sizeof(folder_filename), @@ -656,7 +656,7 @@ db_folder_create(struct db *tdb, struct folder *folder if (bile_write(tdb->bile, DB_FOLDER_RTYPE, folder->id, data, size) != size) panic("save of new folder failed: %d", bile_error(tdb->bile)); - free(data); + xfree(&data); if (getpath(tdb->bile->vrefnum, tdb->bile->filename, &db_filename, false) != 0) --- focusable.c Wed Jun 1 16:40:57 2022 +++ focusable.c Wed Jul 20 09:34:25 2022 @@ -96,7 +96,7 @@ destroy_focusable(struct focusable *focusable) if (nfocusables) focusables = xreallocarray(focusables, sizeof(Ptr), nfocusables); else { - free(focusables); + xfree(&focusables); focusables = NULL; } --- folder.c Tue Jul 19 15:36:09 2022 +++ folder.c Wed Jul 20 22:18:54 2022 @@ -289,7 +289,7 @@ handle_opt: } } - free(file_ids); + xfree(&file_ids); } void @@ -327,7 +327,7 @@ folder_list_files(struct session *s, struct folder *fo file_ids[off + n], &data); bile_unmarshall_object(folder->bile, folder_file_object_fields, nfolder_file_object_fields, data, size, &file, false); - free(data); + xfree(&data); strftime(time, sizeof(time), "%Y-%m-%d", localtime(&file.time)); @@ -424,7 +424,7 @@ folder_upload(struct session *s, struct folder *folder session_pause_return(s, CONTROL_C, "to continue..."); ZDestroy(zs); zs = NULL; - free(upload_path); + xfree(&upload_path); return 0; } @@ -471,7 +471,7 @@ folder_upload(struct session *s, struct folder *folder } if (fp) fclose(fp); - free(data); + xfree(&data); SHA1End(&sha1, (char *)&file.sha1_checksum); session_printf(s, "done.\r\n\r\n"); @@ -495,7 +495,7 @@ file_upload_annotate: if (tmp == NULL) goto file_upload_cancel; strlcpy(file.filename, tmp, sizeof(file.filename)); - free(tmp); + xfree(&tmp); session_output(s, "\r\n", 2); session_flush(s); @@ -503,7 +503,7 @@ file_upload_annotate: if (!folder_file_valid_filename(s, folder, &file, &errorstr)) { session_printf(s, "{{B}}Error:{{/B}} %s\r\n", errorstr); - free(errorstr); + xfree(&errorstr); continue; } @@ -519,7 +519,7 @@ file_upload_annotate: if (tmp == NULL) goto file_upload_cancel; strlcpy(file.description, tmp, sizeof(file.description)); - free(tmp); + xfree(&tmp); session_output(s, "\r\n", 2); session_flush(s); @@ -543,7 +543,7 @@ file_upload_annotate: tmp = session_field_input(s, 2048, s->terminal_columns - 1, file.notes, true, 0); if (file.notes != NULL) - free(file.notes); + xfree(&file.notes); file.notes = tmp; session_output(s, "\r\n", 2); session_flush(s); @@ -555,7 +555,7 @@ file_upload_annotate: rtrim(file.notes, "\r\n\t "); if (file.notes[0] == '\0') { - free(file.notes); + xfree(&file.notes); file.notes = NULL; file.notes_size = 0; break; @@ -636,9 +636,9 @@ file_upload_cancel: file_upload_done: if (file.notes) - free(file.notes); + xfree(&file.notes); if (upload_path != NULL) - free(upload_path); + xfree(&upload_path); return file.id; } @@ -675,7 +675,7 @@ folder_file_view(struct session *s, struct folder *fol bile_error(folder->bile)); bile_unmarshall_object(folder->bile, folder_file_object_fields, nfolder_file_object_fields, data, size, &file, true); - free(data); + xfree(&data); dopts = xmalloc(sizeof(opts)); memcpy(dopts, opts, sizeof(opts)); @@ -731,7 +731,7 @@ folder_file_view(struct session *s, struct folder *fol folder->name, path); session_printf(s, "{{B}}Error:{{/B}} Failed opening file\r\n"); - free(path); + xfree(&path); break; } @@ -769,7 +769,7 @@ folder_file_view(struct session *s, struct folder *fol } ZDestroy(zs); zs = NULL; - free(path); + xfree(&path); session_printf(s, "\r\n"); session_flush(s); @@ -848,8 +848,7 @@ folder_find_file_ids(struct folder *folder, size_t *nf struct file_name_map { unsigned long id; char filename[10]; /* only sorted to 10 character places */ - }; - struct file_name_map *name_map = NULL, tmp_map; + } *name_map = NULL, tmp_map; size_t n, size; short i, j; char *data; @@ -867,8 +866,8 @@ folder_find_file_ids(struct folder *folder, size_t *nf bile_read_alloc(folder->bile, FOLDER_FILE_RTYPE, o->id, &data); bile_unmarshall_object(folder->bile, folder_file_object_fields, nfolder_file_object_fields, data, o->size, &file, false); - free(data); - free(o); + xfree(&data); + xfree(&o); name_map[n].id = file.id; strlcpy(name_map[n].filename, file.filename, @@ -920,10 +919,10 @@ folder_file_save(struct folder *folder, struct folder_ size) != size) { warn("bile_write of new file failed! %d", bile_error(folder->bile)); file->id = 0; - free(data); + xfree(&data); return false; } - free(data); + xfree(&data); bile_flush(folder->bile, true); @@ -939,11 +938,11 @@ folder_file_save(struct folder *folder, struct folder_ warn("FSRename(%s, 0, %s) failed: %d", temp_path, new_name, ret); bile_delete(folder->bile, FOLDER_FILE_RTYPE, file->id); file->id = 0; - free(new_name); + xfree(&new_name); return false; } - free(new_name); + xfree(&new_name); return true; } @@ -967,9 +966,9 @@ folder_delete_file(struct session *s, struct folder *f session_log(s, "[%s] Failed deleting %s: %d", folder->name, path, ret); - free(path); + xfree(&path); if (file->notes != NULL) - free(file->notes); + xfree(&file->notes); session_log(s, "[%s] Deleted file %s (%ld)", folder->name, file->filename, file->id); @@ -1024,15 +1023,15 @@ folder_file_valid_filename(struct session *session, for (n = 0; o = bile_get_nth_of_type(folder->bile, n, FOLDER_FILE_RTYPE); n++) { if (o->id == file->id) { - free(o); + xfree(&o); continue; } bile_read_alloc(folder->bile, FOLDER_FILE_RTYPE, o->id, &data); bile_unmarshall_object(folder->bile, folder_file_object_fields, nfolder_file_object_fields, data, o->size, &tfile, false); - free(data); - free(o); + xfree(&data); + xfree(&o); if (strcasecmp(file->filename, tfile.filename) == 0) { *error = xstrdup("filename is already taken"); --- mail.c Tue Jul 19 15:36:51 2022 +++ mail.c Wed Jul 20 09:37:20 2022 @@ -69,11 +69,11 @@ void mail_free_message_strings(struct mail_message *msg) { if (msg->subject != NULL) { - free(msg->subject); + xfree(&msg->subject); msg->subject = NULL; } if (msg->body != NULL) { - free(msg->body); + xfree(&msg->body); msg->body = NULL; } } @@ -104,13 +104,13 @@ get_id: if (sscanf(tmp, "%d", &ret) != 1 || ret < 1 || ret > nmsgs) { session_printf(s, "{{B}}Invalid message ID{{/B}} (^C to cancel)\r\n"); session_flush(s); - free(tmp); + xfree(&tmp); goto get_id; } get_id_done: if (tmp != NULL) - free(tmp); + xfree(&tmp); return ret; } @@ -147,7 +147,7 @@ mail_menu(struct session *s) while (!done && !s->ending) { if (find_message_ids) { if (mail_ids != NULL) - free(mail_ids); + xfree(&mail_ids); nmsgs = mail_find_ids_for_user(s->user, &nmail_ids, &mail_ids, page * MSGS_PER_PAGE, MSGS_PER_PAGE, false); /* ceil(nmsgs / MSGS_PER_PAGE) */ @@ -236,7 +236,7 @@ handle_opt: } if (mail_ids != NULL) - free(mail_ids); + xfree(&mail_ids); } void @@ -267,7 +267,7 @@ mail_compose_start: tmp = session_field_input(s, DB_USERNAME_LENGTH + 1, DB_USERNAME_LENGTH + 1, to_username, false, 0); if (to_username != NULL) - free(to_username); + xfree(&to_username); to_username = tmp; session_output(s, "\r\n", 2); session_flush(s); @@ -279,12 +279,12 @@ mail_compose_start: session_printf(s, "{{B}}Error:{{/B}}{{#}} No such user \"%s\" " "(^C to cancel)\r\n", to_username); session_flush(s); - free(to_username); + xfree(&to_username); to_username = NULL; continue; } msg.recipient_user_id = to_user->id; - free(to_user); + xfree(&to_user); break; } @@ -294,7 +294,7 @@ mail_compose_start: tmp = session_field_input(s, 50, 50, msg.subject, false, 0); if (msg.subject != NULL) - free(msg.subject); + xfree(&msg.subject); msg.subject = tmp; session_output(s, "\r\n", 2); session_flush(s); @@ -308,7 +308,7 @@ mail_compose_start: session_printf(s, "{{B}}Error:{{/B}} Subject cannot " "be blank (^C to cancel)\r\n"); session_flush(s); - free(msg.subject); + xfree(&msg.subject); msg.subject = NULL; continue; } @@ -324,7 +324,7 @@ mail_compose_start: tmp = session_field_input(s, 2048, s->terminal_columns - 1, msg.body, true, 0); if (msg.body != NULL) - free(msg.body); + xfree(&msg.body); msg.body = tmp; session_output(s, "\r\n", 2); session_flush(s); @@ -338,7 +338,7 @@ mail_compose_start: session_printf(s, "{{B}}Error:{{/B}} Message cannot " "be blank (^C to cancel)\r\n"); session_flush(s); - free(msg.body); + xfree(&msg.body); msg.body = NULL; continue; } @@ -394,7 +394,7 @@ mail_compose_start: mail_compose_done: if (to_username != NULL) - free(to_username); + xfree(&to_username); mail_free_message_strings(&msg); } @@ -421,7 +421,7 @@ mail_list(struct session *s, size_t nmail_ids, unsigne break; bile_unmarshall_object(db->bile, mail_object_fields, nitems(mail_object_fields), data, size, &msg, true); - free(data); + xfree(&data); user = user_find_username(msg.sender_user_id); strftime(time, sizeof(time), "%b %d", localtime(&msg.time)); @@ -473,7 +473,7 @@ mail_read(struct session *s, unsigned long id, short i bile_unmarshall_object(db->bile, mail_object_fields, nitems(mail_object_fields), data, size, &msg, true); - free(data); + xfree(&data); sender = user_find_username(msg.sender_user_id); recipient = user_find_username(msg.recipient_user_id); @@ -522,7 +522,7 @@ mail_read(struct session *s, unsigned long id, short i mail_compose(s, sender->username, reply_subject, NULL); - free(reply_subject); + xfree(&reply_subject); break; case 'd': if (bile_delete(db->bile, DB_MESSAGE_RTYPE, msg.id) == 0) @@ -626,7 +626,7 @@ mail_find_ids_for_user(struct user *user, size_t *nmai id = o->id; bile_read(db->bile, DB_MESSAGE_RTYPE, id, (char *)&msg_user_id, sizeof(msg_user_id)); - free(o); + xfree(&o); if (msg_user_id != user->id) continue; @@ -671,7 +671,7 @@ mail_find_ids_for_user(struct user *user, size_t *nmai if (nmail_ids != NULL) *nmail_ids = 0; if (mail_ids != NULL) - free(*mail_ids); + xfree(&(*mail_ids)); } else { if (mail_ids != NULL) { for (j = offset, i = 0; j < nmsgs_for_user; j++, i++) --- main.c Tue Jul 19 15:21:55 2022 +++ main.c Thu Jul 21 08:47:03 2022 @@ -121,6 +121,7 @@ main(void) SystemTask(); if (!GetNextEvent(everyEvent, &event)) { + xfree_verify(); blanker_idle(); continue; } @@ -261,7 +262,7 @@ handle_menu(long menu_id) vers_s = xmalloc(100); sprintf(vers_s, "%s %s", PROGRAM_NAME, get_version(true)); note("%s", vers_s); - free(vers_s); + xfree(&vers_s); ret = true; break; @@ -292,7 +293,7 @@ handle_menu(long menu_id) } } - free(tfocusables); + xfree(&tfocusables); ExitToShell(); break; } --- session.c Tue Jul 19 16:09:43 2022 +++ session.c Wed Jul 20 17:05:44 2022 @@ -91,7 +91,7 @@ session_create(char *node, char *via, struct node_func session = xmalloczero(sizeof(struct session)); session->uthread = uthread_add(session_run, session); if (!session->uthread) { - free(session); + xfree(&session); return NULL; } @@ -350,7 +350,7 @@ session_close(struct session *session) panic("session_close failed to find session to remove"); nsessions--; - free(session); + xfree(&session); logger_update_title(); } @@ -498,7 +498,7 @@ session_output_view_or_printf(struct session *session, o = bile_find(db->bile, DB_TEXT_TYPE, id); if (o == NULL || o->size == 0) { if (o) - free(0); + xfree(&o); if (format == NULL) return 0; @@ -513,17 +513,17 @@ session_output_view_or_printf(struct session *session, view = xmalloc(o->size + 1); size = bile_read_object(db->bile, o, view, o->size); view[size] = '\0'; - free(o); + xfree(&o); size = session_expand_template(session, view, &output); if (!size) { - free(view); + xfree(&view); return 0; } size = session_output(session, output, size); - free(output); - free(view); + xfree(&output); + xfree(&view); return size; } @@ -841,7 +841,7 @@ append_char: } field_input_bail: - free(field); + xfree(&field); return NULL; } @@ -873,7 +873,7 @@ session_login(struct session *s) if (username[0] == '\0') { n--; - free(username); + xfree(&username); username = NULL; continue; } @@ -881,7 +881,7 @@ session_login(struct session *s) if (strcmp(username, GUEST_USERNAME) == 0) { session_log(s, "Successful guest login in as %s", username); - free(username); + xfree(&username); return AUTH_USER_GUEST; } @@ -889,7 +889,7 @@ session_login(struct session *s) strcmp(username, "new") == 0) && db->config.open_signup) { session_log(s, "Successful guest signup login in as %s", username); - free(username); + xfree(&username); return AUTH_USER_SIGNUP; } else { user = user_find_by_username(username); @@ -904,7 +904,7 @@ session_login(struct session *s) if (s->autologin_username[0]) { if (user) { - free(username); + xfree(&username); s->user = user; session_log(s, "Automatically logged in as %s", s->autologin_username); @@ -943,12 +943,12 @@ session_login(struct session *s) len = strlen(username); memset(username, 0, len); - free(username); + xfree(&username); username = NULL; len = strlen(password); memset(password, 0, len); - free(password); + xfree(&password); password = NULL; if (s->user) { @@ -964,9 +964,9 @@ session_login(struct session *s) login_bail: if (username != NULL) - free(username); + xfree(&username); if (password != NULL) - free(password); + xfree(&password); if (!s->ban_node_source) { if (session_idled_out(s)) { session_printf(s, "\r\nLogin timed out after %d seconds\r\n", @@ -990,12 +990,12 @@ session_expand_template(struct session *session, const size_t tmpllen, retsize, retpos; size_t vallen; short n, invar = 0, varlen = 0, doif, sep; - char *varseek, *curvarpos, *val; + char *varseek, *curvarpos, *val, *data; bool end_expansion = false; retsize = 0; retpos = 0; - *ret = NULL; + data = NULL; tmpllen = strlen(tmpl); for (n = 0; n < tmpllen; n++) { @@ -1012,12 +1012,12 @@ session_expand_template(struct session *session, const varlen = 0; n++; } else if (tmpl[n] == '\r' && tmpl[n + 1] != '\n') { - EXPAND_TO_FIT(*ret, retsize, retpos, 2, 64); - (*ret)[retpos++] = '\r'; - (*ret)[retpos++] = '\n'; + EXPAND_TO_FIT(data, retsize, retpos, 2, 64); + data[retpos++] = '\r'; + data[retpos++] = '\n'; } else { - EXPAND_TO_FIT(*ret, retsize, retpos, 1, 64); - (*ret)[retpos++] = tmpl[n]; + EXPAND_TO_FIT(data, retsize, retpos, 1, 64); + data[retpos++] = tmpl[n]; } continue; @@ -1083,13 +1083,14 @@ expand_var: } if (vallen) { - EXPAND_TO_FIT(*ret, retsize, retpos, vallen, 128); - memcpy(*ret + retpos, val, vallen); + EXPAND_TO_FIT(data, retsize, retpos, vallen, 128); + memcpy(data + retpos, val, vallen); retpos += vallen; } } - (*ret)[retpos] = '\0'; + data[retpos] = '\0'; + *ret = data; return retpos; } @@ -1235,7 +1236,7 @@ session_page_sysop(struct session *s) page_done: if (message != NULL) - free(message); + xfree(&message); } void @@ -1283,7 +1284,7 @@ session_recents(struct session *s) session_flush(s); session_pause_return(s, 0, NULL); - free(ids); + xfree(&ids); } void --- settings.c Tue Jul 19 15:37:11 2022 +++ settings.c Wed Jul 20 09:53:01 2022 @@ -174,7 +174,7 @@ get_input: session_printf(s, "%s is too long (%d max, ^C to cancel)", sf->name, sf->max - 1); - free(input); + xfree(&input); goto get_input; } strlcpy(new_data + sf->off, input, sf->max); @@ -203,14 +203,14 @@ get_input: session_printf(s, "%s must be at least %d (^C to cancel)\r\n", sf->name, sf->min); - free(input); + xfree(&input); goto get_input; } if (lval > sf->max) { session_printf(s, "%s must be less than %d (^C to cancel)\r\n", sf->name, sf->max); - free(input); + xfree(&input); goto get_input; } if (sf->type == CONFIG_TYPE_LONG) { @@ -273,10 +273,10 @@ get_input: if (input[0] == '\0') { lval = 0; - free(input); + xfree(&input); } else { lval = ip2long(input); - free(input); + xfree(&input); if (lval == 0) { session_printf(s, "Invalid IP address (^C to cancel)\r\n"); @@ -293,7 +293,7 @@ get_input: } if (input != NULL) - free(input); + xfree(&input); if (s->ending) { any_changes = false; goto done; @@ -305,7 +305,7 @@ done: *result = new_data; return 0; } else { - free(new_data); + xfree(&(*new_data)); *result = NULL; return -1; } @@ -379,7 +379,7 @@ view_editor_show(size_t id, char *title) HLock(view_editor->te); InvalRect(&(*(view_editor->te))->viewRect); HUnlock(view_editor->te); - free(view); + xfree(&view); } bounds.left = bounds.right; @@ -540,5 +540,5 @@ view_editor_close(struct focusable *focusable, EventRe TEDispose(view_editor->te); destroy_focusable(focusable); - free(view_editor); + xfree(&view_editor); } --- signup.c Tue Jul 19 15:54:17 2022 +++ signup.c Wed Jul 20 09:40:11 2022 @@ -56,7 +56,7 @@ signup(struct session *s) if (!user_valid_username(NULL, username, &error)) { session_printf(s, "{{B}}Error:{{/B}} %s\r\n", error); - free(error); + xfree(&error); continue; } @@ -76,7 +76,7 @@ signup(struct session *s) if (password[0] == '\0') { session_printf(s, "{{B}}Error:{{/B}} " "Password cannot be blank\r\n"); - free(password); + xfree(&password); continue; } @@ -93,8 +93,8 @@ signup(struct session *s) if (strcmp(password_confirm, password) != 0) { session_printf(s, "{{B}}Error:{{/B}} " "Passwords do not match\r\n"); - free(password); - free(password_confirm); + xfree(&password); + xfree(&password_confirm); continue; } @@ -113,11 +113,11 @@ signup(struct session *s) signup_done: if (username != NULL) - free(username); + xfree(&username); if (password != NULL) - free(password); + xfree(&password); if (password_confirm != NULL) - free(password_confirm); + xfree(&password_confirm); return user; } --- sysop.c Wed Jul 13 09:53:41 2022 +++ sysop.c Wed Jul 20 09:41:03 2022 @@ -105,7 +105,7 @@ sysop_edit_settings(struct session *s) session_log(s, "Changed BBS settings"); session_printf(s, "Successfully saved changes to BBS Settings, " "restart to take effect\r\n"); - free(new_config); + xfree(&new_config); } void @@ -133,7 +133,7 @@ sysop_edit_boards(struct session *s) * list of boards may change */ if (dopts != NULL) - free(dopts); + xfree(&dopts); dopts = xmalloc(sizeof(opts) + (db->nboards * sizeof(struct session_menu_option))); for (n = 0; n < db->nboards; n++) { @@ -160,7 +160,7 @@ sysop_edit_boards(struct session *s) board, sizeof(struct board), (void *)&new_board, "New Board", "Sysop:Boards:New"); if (ret != 0) { - free(board); + xfree(&board); continue; } @@ -170,8 +170,8 @@ sysop_edit_boards(struct session *s) session_log(s, "Created new board %ld: %s", new_board->id, new_board->name); - free(board); - free(new_board); + xfree(&board); + xfree(&new_board); break; case '0': case '1': @@ -206,11 +206,11 @@ sysop_edit_boards(struct session *s) if (bile_write(db->bile, DB_BOARD_RTYPE, new_board->id, data, size) != size) panic("save of board failed: %d", bile_error(db->bile)); - free(data); + xfree(&data); session_log(s, "Saved changes to board %ld: %s", new_board->id, new_board->name); - free(new_board); + xfree(&new_board); break; } if (!found) { @@ -253,7 +253,7 @@ sysop_edit_folders(struct session *s) * list of boards may change */ if (dopts != NULL) - free(dopts); + xfree(&dopts); dopts = xmalloc(sizeof(opts) + (db->nboards * sizeof(struct session_menu_option))); for (n = 0; n < db->nfolders; n++) { @@ -280,7 +280,7 @@ sysop_edit_folders(struct session *s) folder, sizeof(struct folder), (void *)&new_folder, "New Folder", "Sysop:Folders:New"); if (ret != 0) { - free(folder); + xfree(&folder); continue; } @@ -290,8 +290,8 @@ sysop_edit_folders(struct session *s) session_log(s, "Created new folder %ld: %s", new_folder->id, new_folder->name); - free(folder); - free(new_folder); + xfree(&folder); + xfree(&new_folder); break; case '0': case '1': @@ -327,11 +327,11 @@ sysop_edit_folders(struct session *s) data, size) != size) panic("save of folder failed: %d", bile_error(db->bile)); - free(data); + xfree(&data); session_log(s, "Saved changes to folder %ld: %s", new_folder->id, new_folder->name); - free(new_folder); + xfree(&new_folder); break; } if (!found) { @@ -471,7 +471,7 @@ handle_opt: } if (all_user_ids != NULL) - free(all_user_ids); + xfree(&all_user_ids); } size_t --- telnet.c Sun Jul 17 20:12:36 2022 +++ telnet.c Wed Jul 20 09:41:17 2022 @@ -197,7 +197,7 @@ telnet_atexit(void) if (node->state > TELNET_PB_STATE_UNUSED) _TCPRelease(&telnet_exit_pb, node->stream, nil, nil, false); - free(node); + xfree(&node); telnet_nodes[n] = NULL; } @@ -877,7 +877,7 @@ telnet_print_busy(struct telnet_node *node) if (data[n] == '\r' && data[n + 1] != '\n') node->obuf[olen++] = '\n'; } - free(data); + xfree(&data); } node->tcp_wds[0].ptr = (Ptr)&node->obuf; --- user.c Tue Jul 19 15:38:19 2022 +++ user.c Wed Jul 20 09:42:02 2022 @@ -52,7 +52,7 @@ user_cache_usernames(void) size_t nuser, len; if (db->username_cache != NULL) - free(db->username_cache); + xfree(&db->username_cache); db->nusers = bile_count_by_type(db->bile, DB_USER_RTYPE); db->username_cache = xmalloczero(sizeof(struct username_cache) * @@ -71,7 +71,7 @@ user_cache_usernames(void) strncpy(muser->username, user.username, sizeof(muser->username)); - free(o); + xfree(&o); nuser++; } } @@ -109,7 +109,7 @@ user_find(unsigned long id) sizeof(struct user)); user = xmalloczero(sizeof(struct user)); memcpy(user, data, sizeof(struct user)); - free(data); + xfree(&data); return user; } @@ -173,7 +173,7 @@ user_authenticate(struct user *user, const char *passw memset(&hash, 0, sizeof(hash)); memset(salted, 0, slen); - free(salted); + xfree(&salted); if (res == 0) return AUTH_USER_OK; @@ -205,7 +205,7 @@ user_set_password(struct user *user, const char *passw memcpy(salted + sizeof(salt) - 1, password, plen); SHA256Data((const u_int8_t *)salted, slen, hash); memset(salted, 0, slen); - free(salted); + xfree(&salted); memcpy(&user->password_hash, &hash, sizeof(user->password_hash)); @@ -258,7 +258,7 @@ user_valid_username(struct user *user, char *username, if ((ouser = user_find_by_username(username))) { ouser_id = ouser->id; - free(ouser); + xfree(&ouser); if (user == NULL || ouser_id != user->id) { *error = xstrdup("username is already in use"); @@ -294,7 +294,7 @@ user_first_sysop_username(void) while ((o = bile_get_nth_of_type(db->bile, nuser, DB_USER_RTYPE))) { len = bile_read(db->bile, DB_USER_RTYPE, o->id, (char *)&user, sizeof(user)); - free(o); + xfree(&o); if (user.is_sysop) { ret = xstrdup(user.username); break; @@ -329,7 +329,7 @@ user_change_password(struct session *s, struct user *u if (password[0] == '\0') { session_printf(s, "{{B}}Error:{{/B}} " "Password cannot be blank\r\n"); - free(password); + xfree(&password); password = NULL; continue; } @@ -346,9 +346,9 @@ user_change_password(struct session *s, struct user *u if (strcmp(password_confirm, password) != 0) { session_printf(s, "{{B}}Error:{{/B}} " "Passwords do not match\r\n"); - free(password); + xfree(&password); password = NULL; - free(password_confirm); + xfree(&password_confirm); password_confirm = NULL; continue; } @@ -371,9 +371,9 @@ user_change_password(struct session *s, struct user *u } if (password != NULL) - free(password); + xfree(&password); if (password_confirm != NULL) - free(password_confirm); + xfree(&password_confirm); } void @@ -389,7 +389,7 @@ user_delete(struct user *user) id = o->id; bile_read(db->bile, DB_MESSAGE_RTYPE, id, (char *)&msg_user_id, sizeof(msg_user_id)); - free(o); + xfree(&o); if (msg_user_id != user->id) { n++; --- user_settings.c Tue Jul 19 15:38:50 2022 +++ user_settings.c Wed Jul 20 09:42:39 2022 @@ -94,7 +94,7 @@ get_terminal_size: session_output(s, "\r\n", 2); session_flush(s); if (tsize[0] == '\0') { - free(tsize); + xfree(&tsize); return; } @@ -102,7 +102,7 @@ get_terminal_size: session_printf(s, "Invalid response, must be in format %dx%d\r\n", s->terminal_columns, s->terminal_lines); session_flush(s); - free(tsize); + xfree(&tsize); goto get_terminal_size; } @@ -110,7 +110,7 @@ get_terminal_size: session_printf(s, "Terminal too small, must be at least " "%dx%d\r\n", MIN_TERMINAL_COLUMNS, MIN_TERMINAL_LINES); session_flush(s); - free(tsize); + xfree(&tsize); goto get_terminal_size; } @@ -144,7 +144,7 @@ user_settings_username(struct session *s) if (username[0] == '\0') { session_printf(s, "{{B}}Error:{{/B}} " "Username cannot be blank\r\n"); - free(username); + xfree(&username); username = NULL; continue; } @@ -154,8 +154,8 @@ user_settings_username(struct session *s) if (user_valid_username(s->user, username, &error) != 1) { session_printf(s, "{{B}}Error:{{/B}} %s\r\n", error); - free(error); - free(username); + xfree(&error); + xfree(&username); username = NULL; continue; } @@ -171,7 +171,7 @@ user_settings_username(struct session *s) } if (username != NULL) - free(username); + xfree(&username); } void --- util.c Fri Jul 15 23:23:30 2022 +++ util.c Wed Jul 20 15:46:16 2022 @@ -65,14 +65,36 @@ static const char progress_ditl[] = { static Handle progress_ditl_h = NULL; static DialogPtr progress_dialog = NULL; +static TEHandle track_control_te = NULL; + enum { STOP_ALERT, CAUTION_ALERT, NOTE_ALERT }; -static TEHandle track_control_te = NULL; +#ifdef MALLOC_DEBUG +/* + * List of allocations, updated at xmalloc() and xfree(). If an address + * passed to xfree() isn't in the list, it indicates a double-free. + */ +#define MALLOC_MAP_SIZE 512 +struct malloc_map_e { + unsigned long addr; + unsigned long size; +} malloc_map[MALLOC_MAP_SIZE]; + +/* + * On xfree(&), the pointer will be updated to point here. xfree_verify() + * should be called periodically to detect any writes to it, indicating + * a use-after-free. + */ +#define MALLOC_UAF_SIZE 256 +static char malloc_uaf[MALLOC_UAF_SIZE]; + +#endif + void vwarn(short alert_func, const char *format, va_list ap); /* @@ -87,34 +109,98 @@ util_init(void) HLock(alert_ditl_h); memcpy(*alert_ditl_h, alert_ditl, sizeof(alert_ditl)); HUnlock(alert_ditl_h); + +#ifdef MALLOC_DEBUG + memset(&malloc_map, 0, sizeof(malloc_map)); + memset(&malloc_uaf, 0, sizeof(malloc_uaf)); +#endif } /* * Memory functions */ - + +#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) + void * xmalloc(size_t size) { void *ptr; - +#ifdef MALLOC_DEBUG + unsigned short n; +#endif + if (size == 0) panic("xmalloc: zero size"); - - ptr = malloc(size); + + ptr = NewPtr(size); if (ptr == NULL) panic("xmalloc(%lu) failed", size); - + +#ifdef MALLOC_DEBUG + for (n = 0; n <= MALLOC_MAP_SIZE; n++) { + if (n == MALLOC_MAP_SIZE) + panic("xmalloc(%lu): out of malloc map entries", size); + if (malloc_map[n].addr == 0) { + malloc_map[n].addr = (unsigned long)ptr; + malloc_map[n].size = size; + break; + } + } +#endif + return ptr; } +void +xfree(void *ptrptr) +{ + unsigned long *addr = (unsigned long *)ptrptr; + void *ptr = (void *)*addr; +#ifdef MALLOC_DEBUG + unsigned long n; + + for (n = 0; n <= MALLOC_MAP_SIZE; n++) { + if (n == MALLOC_MAP_SIZE) + panic("xfree(%lu): can't find %lu in alloc map, likely " + "double free()", *addr); + if (malloc_map[n].addr == *addr) { + malloc_map[n].addr = 0; + malloc_map[n].size = 0; + break; + } + } +#endif + + DisposePtr(ptr); + +#ifdef MALLOC_DEBUG + *addr = (unsigned long)&malloc_uaf; +#else + *addr = 0L; +#endif +} + +#ifdef MALLOC_DEBUG +void +xfree_verify(void) +{ + size_t n; + + for (n = 0; n < sizeof(malloc_uaf); n++) + if (malloc_uaf[0] != '\0') + panic("xfree_verify: use-after-free detected"); +} +#endif + void * xmalloczero(size_t size) { - void *ptr = xmalloc(size); + void *ptr; + ptr = xmalloc(size); memset(ptr, 0, size); - + return ptr; } @@ -123,10 +209,13 @@ xcalloc(size_t nmemb, size_t size) { void *ptr; - ptr = calloc(nmemb, size); + if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && + nmemb > 0 && SIZE_MAX / nmemb < size) + panic("xcalloc(%lu, %lu) overflow", nmemb, size); + ptr = xmalloczero(nmemb * size); if (ptr == NULL) panic("xcalloc(%lu, %lu) failed", nmemb, size); - + return ptr; } @@ -134,16 +223,29 @@ void * xrealloc(void *src, size_t size) { void *ptr; + unsigned long n; - ptr = realloc(src, size); - if (ptr == NULL) - panic("realloc(%lu) failed", size); - + ptr = xmalloc(size); + if (src != NULL) { + memcpy(ptr, src, size); +#ifdef MALLOC_DEBUG + for (n = 0; n <= MALLOC_MAP_SIZE; n++) { + if (n == MALLOC_MAP_SIZE) + panic("xrealloc(0x%lx, %lu): can't find in alloc map, " + "double-free or bogus pointer passed", src, size); + if (malloc_map[n].addr == (unsigned long)src) { + malloc_map[n].addr = 0; + malloc_map[n].size = 0; + break; + } + } +#endif + DisposePtr(src); + } + return ptr; } -#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) - void * xreallocarray(void *optr, size_t nmemb, size_t size) { @@ -576,7 +678,7 @@ xGetStringAsLong(short id) c = xGetStringAsChar(id); r = atol(c); - free(c); + xfree(&c); return r; } @@ -623,7 +725,7 @@ getpath(short vRefNum, Str255 fileName, Str255 *ret, b wdir.ioNamePtr = (StringPtr)name; if (PBGetWDInfo(&wdir, 0) != noErr) { warn("Failed looking up directory"); - free(name); + xfree(&name); return 1; } @@ -633,13 +735,13 @@ getpath(short vRefNum, Str255 fileName, Str255 *ret, b if (PBHGetVInfoSync((HParmBlkPtr)&wvol) != noErr) { warn("Failed getting volume info"); - free(name); + xfree(&name); return 1; } if (wvol.ioVSigWord != 0x4244) { warn("Unknown filesystem type 0x%x", wvol.ioVSigWord); - free(name); + xfree(&name); return 1; } @@ -682,17 +784,17 @@ getpath(short vRefNum, Str255 fileName, Str255 *ret, b } } else if (retlen == 0) { (*ret)[0] = 0; - free(tmp); - free(tmpret); - free(name); + xfree(&tmp); + xfree(&tmpret); + xfree(&name); return 0; } CtoPstr(tmpret); memcpy(*ret, tmpret, sizeof(tmpret)); - free(tmp); - free(tmpret); - free(name); + xfree(&tmp); + xfree(&tmpret); + xfree(&name); return 0; } @@ -706,7 +808,7 @@ stat(char *path, struct stat *sb) ppath = xstrdup(path); CtoPstr(ppath); ret = FStat((unsigned char *)ppath, sb); - free(ppath); + xfree(&ppath); return ret; } @@ -861,7 +963,7 @@ copy_file_contents(short source_ref, short dest_ref) break; } - free(buf); + xfree(&buf); if (error && error != eofErr) return error; --- util.h Tue Jul 12 17:10:27 2022 +++ util.h Wed Jul 20 15:08:46 2022 @@ -21,6 +21,8 @@ #include <limits.h> #include <time.h> +#define MALLOC_DEBUG + #ifndef SIZE_MAX #define SIZE_MAX ULONG_MAX #endif @@ -88,6 +90,8 @@ struct stat { void util_init(void); void * xmalloc(size_t); +void xfree(void *ptrptr); +void xfree_verify(void); void * xmalloczero(size_t); void * xcalloc(size_t, size_t); void * xrealloc(void *src, size_t size); --- zmodem.c Thu Jul 21 09:01:34 2022 +++ zmodem.c Thu Jul 21 09:02:46 2022 @@ -1614,6 +1614,6 @@ ZDestroy(struct zmodem_session *zs) if (zs->file) fclose(zs->file); if (zs->upload_file_path) - free(zs->upload_file_path); - free(zs); + xfree(&zs->upload_file_path); + xfree(&zs); }