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 segfault in Signature.__repr__ & __str__ when the signature's encoding is incorrect or unknown (#1205) #1210

Merged
merged 2 commits into from
May 6, 2023
Merged
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
22 changes: 11 additions & 11 deletions src/signature.c
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,11 @@ static PyObject *
Signature__str__(Signature *self)
{
PyObject *name, *email, *str;
name = to_unicode(self->signature->name, self->encoding, NULL);
email = to_unicode(self->signature->email, self->encoding, NULL);
name = to_unicode_safe(self->signature->name, self->encoding);
email = to_unicode_safe(self->signature->email, self->encoding);
assert(name);
assert(email);

str = PyUnicode_FromFormat("%U <%U>", name, email);
Py_DECREF(name);
Py_DECREF(email);
Expand All @@ -241,15 +244,12 @@ static PyObject *
Signature__repr__(Signature *self)
{
PyObject *name, *email, *encoding, *str;
name = to_unicode(self->signature->name, self->encoding, NULL);
email = to_unicode(self->signature->email, self->encoding, NULL);

if (self->encoding) {
encoding = to_unicode(self->encoding, self->encoding, NULL);
} else {
encoding = Py_None;
Py_INCREF(Py_None);
}
name = to_unicode_safe(self->signature->name, self->encoding);
email = to_unicode_safe(self->signature->email, self->encoding);
encoding = to_unicode_safe(self->encoding, self->encoding);
assert(name);
assert(email);
assert(encoding);

str = PyUnicode_FromFormat(
"pygit2.Signature(%R, %R, %lld, %ld, %R)",
Expand Down
27 changes: 27 additions & 0 deletions src/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,33 @@ extern PyTypeObject CommitType;
extern PyTypeObject BlobType;
extern PyTypeObject TagType;

/**
* Attempt to convert a C string to a Python string with the given encoding.
* If the conversion fails, return a fallback string.
*/
PyObject *
to_unicode_safe(const char *value, const char *encoding)
{
PyObject *py_str;

if (!value) {
py_str = PyUnicode_FromString("None");
} else {
py_str = to_unicode(value, encoding, "replace");

if (!py_str) {
assert(PyErr_Occurred());
py_str = PyUnicode_FromString("(error)");
PyErr_Clear();
}
}

assert(!PyErr_Occurred());
assert(py_str);

return py_str;
}

/**
* Return a *newly allocated* C string holding the string contained in the
* 'value' argument.
Expand Down
2 changes: 2 additions & 0 deletions src/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@
/* Utilities */
#define to_unicode(x, encoding, errors) to_unicode_n(x, strlen(x), encoding, errors)

PyObject *to_unicode_safe(const char *value, const char *encoding);

PYGIT2_FN_UNUSED
Py_LOCAL_INLINE(PyObject*)
to_unicode_n(const char *value, size_t len, const char *encoding,
Expand Down
18 changes: 18 additions & 0 deletions test/test_signature.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.

import re
import time

import pytest
Expand Down Expand Up @@ -69,3 +70,20 @@ def test_repr_from_commit(barerepo):

assert repr(signature) == repr(commit.author)
assert repr(signature) == repr(commit.committer)

def test_incorrect_encoding():
gbk_bytes = 'Café'.encode('GBK')

# deliberately specifying a mismatching encoding (mojibake)
signature = Signature(gbk_bytes, "foo@example.com", 999, 0, encoding="utf-8")

# repr() and str() may display junk, but they must not crash
assert re.match(r"pygit2.Signature\('Caf.+', 'foo@example.com', 999, 0, 'utf-8'\)", repr(signature))
assert re.match(r"Caf.+ <foo@example.com>", str(signature))

# deliberately specifying an unsupported encoding
signature = Signature(gbk_bytes, "foo@example.com", 999, 0, encoding="this-encoding-does-not-exist")

# repr() and str() may display junk, but they must not crash
assert "pygit2.Signature('(error)', '(error)', 999, 0, '(error)')" == repr(signature)
assert "(error) <(error)>" == str(signature)