Jiri Daněk created DISPATCH-1821:
------------------------------------

             Summary: Double-free in qd_entity_configure_policy on error
                 Key: DISPATCH-1821
                 URL: https://issues.apache.org/jira/browse/DISPATCH-1821
             Project: Qpid Dispatch
          Issue Type: Bug
          Components: Policy Engine
    Affects Versions: 1.14.0
            Reporter: Jiri Daněk


As far as I can tell, this double-free is near impossible in normal operation 
of the router. Below, it is reproduced using a test, which passes in a value 
for that cannot be converted to a Python bool. There is no way for a legitimate 
user to make this happen.

Regarding a fix, I am not sure what the router should do when it detect invalid 
configuration. Freeing qd->policy is IMO not the right response, because that 
is normally freed in qd_dispatch_free. Printing an error and carying on (that 
now happens for {{maxConnections < 0}}) does not seem right to me either (but 
it's probably needed in case the config is changed through remote management 
interface),

{noformat}
2020-11-02 09:48:51.887312 +0100 ROUTER_CORE (info) In-process subscription 
L/$management
2020-11-02 09:48:51.939210 +0100 ERROR (error) Python: NotImplementedError: 
2020-11-02 09:48:51.941666 +0100 ERROR (error) Traceback (most recent call 
last):
  File "test_module", line 3, in __bool__
NotImplementedError

=================================================================
==27615==ERROR: AddressSanitizer: attempting double-free on 0x602000001810 in 
thread T0:
    #0 0x7f876d5191d7 in __interceptor_free 
(/nix/store/zhbhxp4jgalycwffildy2bwcgmrsmayk-gcc-9.2.0-lib/lib/libasan.so.5+0x10a1d7)
    #1 0x7f876cd33b95 in qd_policy_free ../src/policy.c:136
    #2 0x7f876cd347cf in qd_entity_configure_policy ../src/policy.c:178
    #3 0x4ef6a6 in _DOCTEST_ANON_FUNC_6 ../tests/c_unittests/test_policy.cpp:75
    #4 0x493edd in doctest::Context::run() ../tests/c_unittests/doctest.h:5849
    #5 0x4974e9 in main ../tests/c_unittests/doctest.h:5933
    #6 0x7f876b610d8a in __libc_start_main 
(/nix/store/xg6ilb9g9zhi2zg1dpi4zcp288rhnvns-glibc-2.30/lib/libc.so.6+0x23d8a)
    #7 0x43cd99 in _start 
(/home/jdanek/repos/qpid/qpid-dispatch/cmake-build-debug/tests/c_unittests/c_unittests+0x43cd99)

0x602000001810 is located 0 bytes inside of 5-byte region 
[0x602000001810,0x602000001815)
freed by thread T0 here:
    #0 0x7f876d5191d7 in __interceptor_free 
(/nix/store/zhbhxp4jgalycwffildy2bwcgmrsmayk-gcc-9.2.0-lib/lib/libasan.so.5+0x10a1d7)
    #1 0x7f876cd347c3 in qd_entity_configure_policy ../src/policy.c:177
    #2 0x4ef6a6 in _DOCTEST_ANON_FUNC_6 ../tests/c_unittests/test_policy.cpp:75
    #3 0x493edd in doctest::Context::run() ../tests/c_unittests/doctest.h:5849
    #4 0x4974e9 in main ../tests/c_unittests/doctest.h:5933
    #5 0x7f876b610d8a in __libc_start_main 
(/nix/store/xg6ilb9g9zhi2zg1dpi4zcp288rhnvns-glibc-2.30/lib/libc.so.6+0x23d8a)

previously allocated by thread T0 here:
    #0 0x7f876d4a40b5 in strdup 
(/nix/store/zhbhxp4jgalycwffildy2bwcgmrsmayk-gcc-9.2.0-lib/lib/libasan.so.5+0x950b5)
    #1 0x7f876cec8c99 in py_string_2_c ../src/python_utils.c:35
    #2 0x7f876cce21e2 in qd_entity_get_string ../src/entity.c:49
    #3 0x7f876cce2b5f in qd_entity_opt_string ../src/entity.c:84
    #4 0x7f876cd341b0 in qd_entity_configure_policy ../src/policy.c:161
    #5 0x4ef6a6 in _DOCTEST_ANON_FUNC_6 ../tests/c_unittests/test_policy.cpp:75
    #6 0x493edd in doctest::Context::run() ../tests/c_unittests/doctest.h:5849
    #7 0x4974e9 in main ../tests/c_unittests/doctest.h:5933
    #8 0x7f876b610d8a in __libc_start_main 
(/nix/store/xg6ilb9g9zhi2zg1dpi4zcp288rhnvns-glibc-2.30/lib/libc.so.6+0x23d8a)

SUMMARY: AddressSanitizer: double-free 
(/nix/store/zhbhxp4jgalycwffildy2bwcgmrsmayk-gcc-9.2.0-lib/lib/libasan.so.5+0x10a1d7)
 in __interceptor_free
==27615==ABORTING
Process finished with exit code 1
{noformat}

The test which follows is currently missing PY decrefs. 

{noformat}
#include <Python.h>

#include <thread>

#include "helpers.hpp"
#include "qdr_doctest.h"

extern "C" {
#include <qpid/dispatch/python_embedded.h>
#include <qpid/dispatch/router_core.h>
#include <router_core/router_core_private.h>
#include <terminus_private.h>

#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
}

TEST_CASE("policy") {
    std::thread([]() {
        WithNoMemoryLeaks leaks{};
        QDR               qdr;
        qdr.start();
        qdr.wait();

        {
            auto qd = qdr.qd;

            PyObject *module = Py_CompileString(
                // language: Python
                R"EOT(
class NotBool:
    def __bool__(self):
        raise NotImplementedError()


fake_policy = {
      "maxConnections": 4,
      "policyDir": "/tmp",
      "enableVhostPolicy": 4,
      "enableVhostNamePatterns": NotBool(),
})EOT",
                "test_module", Py_file_input);
            REQUIRE(module != nullptr);

            PyObject *pModuleObj = PyImport_ExecCodeModule("test_module", 
module);
            CHECK(pModuleObj != nullptr);
            PyErr_Print();

            PyObject *pAttrObj = PyObject_GetAttrString(pModuleObj, 
"fake_policy");
            REQUIRE(pAttrObj != nullptr);

            auto *entity = reinterpret_cast<qd_entity_t *>(pAttrObj);
            REQUIRE(qd_entity_has(entity, "enableVhostNamePatterns"));  // 
sanity check for the test input

            // TODO Py Decrefs probably needed somewhere

            const qd_python_lock_state_t lock_state = qd_python_lock();
            qd_error_t                   err = qd_dispatch_configure_policy(qd, 
entity);
            CHECK(err == QD_ERROR_PYTHON);
            qd_python_unlock(lock_state);
        }
        qdr.stop();
    }).join();
}
{noformat}




--
This message was sent by Atlassian Jira
(v8.3.4#803005)

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@qpid.apache.org
For additional commands, e-mail: dev-h...@qpid.apache.org

Reply via email to