refs: fix segfault when using a deleted reference

Before this patch the code below produced a segfault:

  >>> reference.delete()
  >>> reference.name

Now an exception is raised.
This commit is contained in:
J. David Ibáñez 2011-12-19 23:40:27 +01:00
parent 01cb80b4c8
commit 5986688197
3 changed files with 77 additions and 41 deletions

@ -3,6 +3,12 @@ Signature
- Implement equality interface
- In Repository's create_commit/create_tag check signatures encoding is right
References
==========
- Free the git_reference struct.
- Wrap missing functions: git_reference_foreach, git_reference_is_packed,
git_reference_reload
Other
=========
- Make the Py_LOCAL_INLINE macro to work with Python 2.6, 2.7 and 3.1

@ -67,6 +67,17 @@ to_bytes(const char * value)
#define to_encoding(x) PyUnicode_DecodeASCII(x, strlen(x), "strict")
#endif
#define CHECK_REFERENCE(self)\
if (self->reference == NULL) {\
PyErr_SetString(GitError, "deleted reference");\
return NULL;\
}
#define CHECK_REFERENCE_INT(self)\
if (self->reference == NULL) {\
PyErr_SetString(GitError, "deleted reference");\
return -1;\
}
/* Python objects */
typedef struct {
@ -773,26 +784,21 @@ Repository_listall_references(Repository *self, PyObject *args)
/* 3- Create a new PyTuple */
py_result = PyTuple_New(c_result.count);
if (py_result == NULL) {
git_strarray_free(&c_result);
return NULL;
}
if (py_result == NULL)
goto out;
/* 4- Fill it */
for (index=0; index < c_result.count; index++) {
py_string = to_path((c_result.strings)[index]);
if (py_string == NULL) {
Py_XDECREF(py_result);
git_strarray_free(&c_result);
return NULL;
Py_CLEAR(py_result);
goto out;
}
PyTuple_SET_ITEM(py_result, index, py_string);
}
/* 5- Destroy the c_result */
out:
git_strarray_free(&c_result);
/* 6- And return the py_result */
return py_result;
}
@ -811,7 +817,7 @@ Repository_lookup_reference(Repository *self, PyObject *py_name)
/* 2- Lookup */
err = git_reference_lookup(&c_reference, self->repo, c_name);
if (err < 0)
return Error_set(err);
return Error_set_str(err, c_name);
/* 3- Make an instance of Reference and return it */
return wrap_reference(c_reference);
@ -860,7 +866,7 @@ Repository_create_symbolic_reference(Repository *self, PyObject *args)
err = git_reference_create_symbolic(&c_reference, self->repo, c_name,
c_target, 0);
if (err < 0)
return Error_set(err);
return Error_set(err);
/* 3- Make an instance of Reference and return it */
return wrap_reference(c_reference);
@ -2363,16 +2369,15 @@ Reference_delete(Reference *self, PyObject *args)
{
int err;
/* 1- Delete the reference */
CHECK_REFERENCE(self);
/* Delete the reference */
err = git_reference_delete(self->reference);
if (err < 0)
return Error_set(err);
return Error_set(err);
/* 2- Invalidate the pointer */
self->reference = NULL;
/* 3- Return None */
Py_RETURN_NONE;
self->reference = NULL; /* Invalidate the pointer */
Py_RETURN_NONE; /* Return None */
}
static PyObject *
@ -2381,18 +2386,19 @@ Reference_rename(Reference *self, PyObject *py_name)
char *c_name;
int err;
/* 1- Get the C name */
CHECK_REFERENCE(self);
/* Get the C name */
c_name = py_path_to_c_str(py_name);
if (c_name == NULL)
return NULL;
/* 2- Rename */
/* Rename */
err = git_reference_rename(self->reference, c_name, 0);
if (err < 0)
return Error_set(err);
return Error_set(err);
/* 3- Return None */
Py_RETURN_NONE;
Py_RETURN_NONE; /* Return None */
}
static PyObject *
@ -2401,12 +2407,14 @@ Reference_resolve(Reference *self, PyObject *args)
git_reference *c_reference;
int err;
/* 1- Resolve */
CHECK_REFERENCE(self);
/* Resolve */
err = git_reference_resolve(&c_reference, self->reference);
if (err < 0)
return Error_set(err);
return Error_set(err);
/* 2- Make an instance of Reference and return it */
/* Make an instance of Reference and return it */
return wrap_reference(c_reference);
}
@ -2415,14 +2423,16 @@ Reference_get_target(Reference *self)
{
const char * c_name;
/* 1- Get the target */
CHECK_REFERENCE(self);
/* Get the target */
c_name = git_reference_target(self->reference);
if (c_name == NULL) {
PyErr_SetString(PyExc_ValueError, "Not target available");
PyErr_SetString(PyExc_ValueError, "no target available");
return NULL;
}
/* 2- Make a PyString and return it */
/* Make a PyString and return it */
return to_path(c_name);
}
@ -2432,25 +2442,27 @@ Reference_set_target(Reference *self, PyObject *py_name)
char *c_name;
int err;
/* 1- Get the C name */
CHECK_REFERENCE_INT(self);
/* Get the C name */
c_name = py_path_to_c_str(py_name);
if (c_name == NULL)
return -1;
/* 2- Set the new target */
/* Set the new target */
err = git_reference_set_target(self->reference, c_name);
if (err < 0) {
Error_set(err);
return -1;
}
/* 3- All OK */
return 0;
}
static PyObject *
Reference_get_name(Reference *self)
{
CHECK_REFERENCE(self);
return to_path(git_reference_name(self->reference));
}
@ -2459,7 +2471,9 @@ Reference_get_oid(Reference *self)
{
const git_oid *oid;
/* 1- Get the oid (only for "direct" references) */
CHECK_REFERENCE(self);
/* Get the oid (only for "direct" references) */
oid = git_reference_oid(self->reference);
if (oid == NULL) {
PyErr_SetString(PyExc_ValueError,
@ -2468,7 +2482,7 @@ Reference_get_oid(Reference *self)
return NULL;
}
/* 2- Convert and return it */
/* Convert and return it */
return git_oid_to_python(oid->id);
}
@ -2479,20 +2493,21 @@ Reference_set_oid(Reference *self, PyObject *py_hex)
int err;
size_t len;
/* 1- Get the oid */
CHECK_REFERENCE_INT(self);
/* Get the oid */
len = py_str_to_git_oid(py_hex, &oid);
TODO_SUPPORT_SHORT_HEXS(len)
if (len == 0)
return -1;
/* 2- Set the oid */
/* Set the oid */
err = git_reference_set_oid(self->reference, &oid);
if (err < 0) {
Error_set(err);
return -1;
}
/* 3- All OK */
return 0;
}
@ -2501,7 +2516,9 @@ Reference_get_hex(Reference *self)
{
const git_oid *oid;
/* 1- Get the oid (only for "direct" references) */
CHECK_REFERENCE(self);
/* Get the oid (only for "direct" references) */
oid = git_reference_oid(self->reference);
if (oid == NULL) {
PyErr_SetString(PyExc_ValueError,
@ -2510,7 +2527,7 @@ Reference_get_hex(Reference *self)
return NULL;
}
/* 2- Convert and return it */
/* Convert and return it */
return git_oid_to_py_str(oid);
}
@ -2519,6 +2536,7 @@ Reference_get_type(Reference *self)
{
git_rtype c_type;
CHECK_REFERENCE(self);
c_type = git_reference_type(self->reference);
return PyInt_FromLong(c_type);
}

@ -31,7 +31,7 @@ from __future__ import absolute_import
from __future__ import unicode_literals
import unittest
from pygit2 import GIT_REF_OID, GIT_REF_SYMBOLIC
from pygit2 import GitError, GIT_REF_OID, GIT_REF_SYMBOLIC
from . import utils
@ -113,6 +113,18 @@ class ReferencesTest(utils.RepoTestCase):
reference.delete()
self.assertFalse('refs/tags/version1' in repo.listall_references())
# Access the deleted reference
self.assertRaises(GitError, getattr, reference, 'name')
self.assertRaises(GitError, getattr, reference, 'type')
self.assertRaises(GitError, getattr, reference, 'oid')
self.assertRaises(GitError, setattr, reference, 'oid', LAST_COMMIT)
self.assertRaises(GitError, getattr, reference, 'hex')
self.assertRaises(GitError, getattr, reference, 'target')
self.assertRaises(GitError, setattr, reference, 'target', "a/b/c")
self.assertRaises(GitError, reference.delete)
self.assertRaises(GitError, reference.resolve)
self.assertRaises(GitError, reference.rename, "refs/tags/version2")
def test_rename(self):
# We add a tag as a new reference that points to "origin/master"