Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix memory leak when freeing unknown frame types. #56

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions include/modules/tag.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,16 @@ ID3v2_FrameList* ID3v2_Tag_get_apic_frames(ID3v2_Tag* tag);
/**
* Setter functions
*/
typedef struct _ID3v2_FrameInput
{
const char* id;
const char* flags;
const char* data;
const int size;
} ID3v2_FrameInput;

void ID3v2_Tag_set_frame(ID3v2_Tag* tag, const ID3v2_FrameInput* input);

typedef struct _ID3v2_TextFrameInput
{
const char* id;
Expand Down
1 change: 1 addition & 0 deletions src/modules/frame.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,5 +104,6 @@ void ID3v2_Frame_free(ID3v2_Frame* frame)
// Unknown frame id, naively try our best to free it
free(frame->header);
free(frame->data);
free(frame);
}
}
58 changes: 23 additions & 35 deletions src/modules/tag.c
Original file line number Diff line number Diff line change
Expand Up @@ -198,12 +198,8 @@ ID3v2_FrameList* ID3v2_Tag_get_apic_frames(ID3v2_Tag* tag)
/**
* Setter functions
*/
void ID3v2_Tag_set_text_frame(ID3v2_Tag* tag, ID3v2_TextFrameInput* input)
void ID3v2_Tag_set_frame_internal(ID3v2_Tag* tag, ID3v2_Frame* new_frame, ID3v2_Frame* existing_frame)
{
ID3v2_TextFrame* new_frame = TextFrame_new(input->id, input->flags, input->text);
ID3v2_TextFrame* existing_frame =
(ID3v2_TextFrame*) FrameList_get_frame_by_id(tag->frames, input->id);

if (existing_frame == NULL)
{
FrameList_add_frame(tag->frames, (ID3v2_Frame*) new_frame);
Expand All @@ -221,6 +217,26 @@ void ID3v2_Tag_set_text_frame(ID3v2_Tag* tag, ID3v2_TextFrameInput* input)
}
}

void ID3v2_Tag_set_frame(ID3v2_Tag* tag, const ID3v2_FrameInput* input)
{
ID3v2_Frame* new_frame = (ID3v2_Frame*) malloc(sizeof(ID3v2_Frame));
new_frame->header = FrameHeader_new(input->id, input->flags, input->size);
new_frame->data = (char*) malloc(input->size * sizeof(char));
memcpy(new_frame->data, input->data, input->size);

ID3v2_Frame* existing_frame = FrameList_get_frame_by_id(tag->frames, input->id);
ID3v2_Tag_set_frame_internal(tag, new_frame, existing_frame);
}

void ID3v2_Tag_set_text_frame(ID3v2_Tag* tag, ID3v2_TextFrameInput* input)
{
ID3v2_TextFrame* new_frame = TextFrame_new(input->id, input->flags, input->text);
ID3v2_TextFrame* existing_frame =
(ID3v2_TextFrame*) FrameList_get_frame_by_id(tag->frames, input->id);

ID3v2_Tag_set_frame_internal(tag, (ID3v2_Frame*)new_frame, (ID3v2_Frame*)existing_frame);
}

void ID3v2_Tag_set_artist(ID3v2_Tag* tag, const char* artist)
{
ID3v2_Tag_set_text_frame(
Expand Down Expand Up @@ -338,21 +354,7 @@ void ID3v2_Tag_set_comment_frame(ID3v2_Tag* tag, ID3v2_CommentFrameInput* input)
CommentFrame_new(input->flags, input->language, input->short_description, input->comment);
ID3v2_CommentFrame* existing_frame = ID3v2_Tag_get_comment_frame(tag);

if (existing_frame == NULL)
{
FrameList_add_frame(tag->frames, (ID3v2_Frame*) new_frame);
tag->header->tag_size += new_frame->header->size;
}
else
{
FrameList_replace_frame(
tag->frames,
(ID3v2_Frame*) existing_frame,
(ID3v2_Frame*) new_frame
);
tag->header->tag_size += (new_frame->header->size - existing_frame->header->size);
ID3v2_Frame_free((ID3v2_Frame*) existing_frame);
}
ID3v2_Tag_set_frame_internal(tag, (ID3v2_Frame*)new_frame, (ID3v2_Frame*)existing_frame);
}

void ID3v2_Tag_add_comment_frame(ID3v2_Tag* tag, ID3v2_CommentFrameInput* input)
Expand Down Expand Up @@ -391,21 +393,7 @@ void ID3v2_Tag_set_apic_frame(ID3v2_Tag* tag, ID3v2_ApicFrameInput* input)
);
ID3v2_ApicFrame* existing_frame = ID3v2_Tag_get_album_cover_frame(tag);

if (existing_frame == NULL)
{
FrameList_add_frame(tag->frames, (ID3v2_Frame*) new_frame);
tag->header->tag_size += new_frame->header->size;
}
else
{
FrameList_replace_frame(
tag->frames,
(ID3v2_Frame*) existing_frame,
(ID3v2_Frame*) new_frame
);
tag->header->tag_size += (new_frame->header->size - existing_frame->header->size);
ID3v2_Frame_free((ID3v2_Frame*) existing_frame);
}
ID3v2_Tag_set_frame_internal(tag, (ID3v2_Frame*)new_frame, (ID3v2_Frame*)existing_frame);
}

void ID3v2_Tag_add_apic_frame(ID3v2_Tag* tag, ID3v2_ApicFrameInput* input)
Expand Down
15 changes: 15 additions & 0 deletions test/assertion_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,21 @@ void assert_frame_header(ID3v2_Frame* frame, Frame_header_assertion comparison)
assert(memcmp(frame->header->flags, comparison.flags, ID3v2_FRAME_HEADER_FLAGS_LENGTH) == 0);
}

void assert_frame(ID3v2_Frame* frame, ID3v2_FrameInput* comparison) {
// Header
assert_frame_header(
frame,
(Frame_header_assertion){
.id = comparison->id,
.size = comparison->size,
.flags = comparison->flags,
}
);

// Data
assert(memcmp(frame->data, comparison->data, comparison->size) == 0);
}

void assert_text_frame(ID3v2_TextFrame* frame, ID3v2_TextFrameInput* comparison)
{
const char encoding = has_bom(comparison->text) ? ID3v2_ENCODING_UNICODE : ID3v2_ENCODING_ISO;
Expand Down
1 change: 1 addition & 0 deletions test/assertion_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ typedef struct _Frame_header_assertion
const char* flags;
} Frame_header_assertion;

void assert_frame(ID3v2_Frame* frame, ID3v2_FrameInput* comparison);
void assert_frame_header(ID3v2_Frame* frame, Frame_header_assertion comparison);
void assert_text_frame(ID3v2_TextFrame* frame, ID3v2_TextFrameInput* comparison);
void assert_comment_frame(ID3v2_CommentFrame* frame, ID3v2_CommentFrameInput* comparison);
Expand Down
25 changes: 25 additions & 0 deletions test/set_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,31 @@ void edit_test()

void new_tag_test()
{
const char* frame_id = "GEOB";
const char frame_data[8] = {0xD, 0xE, 0xA, 0xD, 0xB, 0xE, 0xE, 0xF};
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't just use a string here because I thought it made it clearer that this isn't supposed to be testing text frames.


// set a new, non-standard frame
ID3v2_Tag* tag = ID3v2_Tag_new_empty();
ID3v2_FrameInput new_frame = {
.id = frame_id,
.flags = "\0\0",
.data = &frame_data[0],
.size = 8
};
ID3v2_Tag_set_frame(tag, &new_frame);

// verify the frame
assert_frame(
ID3v2_Tag_get_frame(tag, frame_id),
&(ID3v2_FrameInput){
.id = frame_id,
.flags = "\0\0",
.data = &frame_data[0],
.size = 8,
}
);

ID3v2_Tag_free(tag);
}

void set_test_main()
Expand Down