Skip to content

Commit

Permalink
clipmenud: Ensure consistent memory management for clipboard text
Browse files Browse the repository at this point in the history
Previously, clipboard text was sometimes freed with XFree() and
sometimes with free(), potentially causing double frees when using the
INCR protocol for large clippings.

Convert the XGetWindowProperty()-allocated memory to a
malloc()-allocated buffer immediately in get_clipboard_text(), and
always free it with free().

Fixes #241.
  • Loading branch information
cdown committed Dec 14, 2024
1 parent 64c335a commit bfbb51f
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 4 deletions.
22 changes: 18 additions & 4 deletions src/clipmenud.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,20 @@ static char *get_clipboard_text(Atom clip_atom) {
return NULL;
}

return (char *)cur_text;
// Usually the lifecycle here would be ended by XFree(), but we need
// consistency with INCR handling, which uses malloc(), so we can always
// use free(). As such, move to the malloc() pool.
if (!cur_text) {
return NULL;
}

char *m_cur_text = malloc(nitems + 1);
expect(m_cur_text);
memcpy(m_cur_text, cur_text, nitems);
m_cur_text[nitems] = '\0';
XFree(cur_text);

return m_cur_text;
}

/**
Expand Down Expand Up @@ -243,7 +256,7 @@ static uint64_t store_clip(char *text) {
}

if (last_text) {
XFree(last_text);
free(last_text);
}
last_text = text;
last_text_time = current_time;
Expand All @@ -263,7 +276,7 @@ static void incr_receive_finish(struct incr_transfer *it) {
}

it_dbg(it, "Finished (bytes buffered: %zu)\n", it->data_size);
_drop_(free) char *text = malloc(it->data_size + 1);
char *text = malloc(it->data_size + 1);
expect(text);
memcpy(text, it->data, it->data_size);
text[it->data_size] = '\0';
Expand All @@ -280,6 +293,7 @@ static void incr_receive_finish(struct incr_transfer *it) {
}
} else {
it_dbg(it, "Clipboard text is whitespace only, ignoring\n");
free(text);
}

free(it->data);
Expand Down Expand Up @@ -423,7 +437,7 @@ static int handle_property_notify(const XPropertyEvent *pe) {
}
} else {
dbg("Clipboard text is whitespace only, ignoring\n");
XFree(text);
free(text);
}
}

Expand Down
19 changes: 19 additions & 0 deletions tests/x_integration_tests
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,25 @@ len_after=$(xsel -po | wc -c)
# Make sure we got the whole thing
(( len_before == len_after ))

# Issue #241. Put a large clipboard selection via INCR, then put something else
# small immediately after.
xsel -bc
settle
check_nr_clips 5

# Large selection (INCR)
printf '%.0sa' {1..4001} | xsel -b
long_settle

check_nr_clips 6

# After large selection is stored, put something small that's a possible #
# partial (non-INCR)
printf a | xsel -b
settle

check_nr_clips 6

if (( _UNSHARED )); then
umount -l /tmp
fi

0 comments on commit bfbb51f

Please sign in to comment.