{"action":"opened","issue":{"url":"https://api.github.com/repos/vrogier/ocilib/issues/141","repository_url":"https://api.github.com/repos/vrogier/ocilib","labels_url":"https://api.github.com/repos/vrogier/ocilib/issues/141/labels{/name}","comments_url":"https://api.github.com/repos/vrogier/ocilib/issues/141/comments","events_url":"https://api.github.com/repos/vrogier/ocilib/issues/141/events","html_url":"https://github.com/vrogier/ocilib/issues/141","id":295421383,"number":141,"title":"ocilib::Raw aka std::vector<unsigned char> interface problems","user":{"login":"kornefalk","id":13331510,"avatar_url":"https://avatars0.githubusercontent.com/u/13331510?v=4","gravatar_id":"","url":"https://api.github.com/users/kornefalk","html_url":"https://github.com/kornefalk","followers_url":"https://api.github.com/users/kornefalk/followers","following_url":"https://api.github.com/users/kornefalk/following{/other_user}","gists_url":"https://api.github.com/users/kornefalk/gists{/gist_id}","starred_url":"https://api.github.com/users/kornefalk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kornefalk/subscriptions","organizations_url":"https://api.github.com/users/kornefalk/orgs","repos_url":"https://api.github.com/users/kornefalk/repos","events_url":"https://api.github.com/users/kornefalk/events{/privacy}","received_events_url":"https://api.github.com/users/kornefalk/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2018-02-08T08:20:41Z","updated_at":"2018-02-08T08:20:41Z","closed_at":null,"author_association":"NONE","body":"2 problems\r\n1. While binding Raw, the ocilib::BindObjectAdaptor writes a zero to a position out of allocated memory\r\n1. If passing an empty Raw vector, internal error 15 is returned.\r\n\r\n## Details:\r\n\r\n### Binding\r\nRunning `valgrind --track-origins=yes --log-file=val.log`  shows memory allocation errors\r\n```c++\r\n==4428== Invalid write of size 1\r\n==4428==    at 0x414B6F: ocilib::BindObjectAdaptor<std::vector<unsigned char, std::allocator<unsigned char> > >::SetInData() (ocilib_impl.hpp:4721)\r\ntemplate<class T>\r\nvoid BindObjectAdaptor<T>::SetInData()\r\n{\r\n    if (GetMode() & OCI_BDM_IN)\r\n    {\r\n...\r\n        _data[size] = 0; <== here\r\n    }\r\n}\r\n==4428==    by 0x407855: ocilib::BindsHolder::SetInData() (ocilib_impl.hpp:4880)\r\n==4428==    by 0x407E15: ocilib::Statement::SetInData() const (ocilib_impl.hpp:5878)\r\n==4428==    by 0x407985: ocilib::Statement::ExecutePrepared() (ocilib_impl.hpp:4992)\r\n==4428==    by 0x40382D: testInsert(ocilib::Connection&, std::string&, std::vector<unsigned char, std::allocator<unsigned char> >&) (bugbind.cpp:30)\r\n==4428==    by 0x4043B5: main (bugbind.cpp:134)\r\n==4428==  Address 0x7ee1273 is 0 bytes after a block of size 3 alloc'd\r\n==4428==    at 0x4A07152: operator new[](unsigned long) (vg_replace_malloc.c:363)\r\n==4428==    by 0x4095EE: ocilib::BindObjectAdaptor<std::vector<unsigned char, std::allocator<unsigned char> > >::BindObjectAdaptor(ocilib::Statement const&, std::string const&, unsigned int, std::vector<unsigned char, std::allocator<unsigned char> >&, unsigned int) (ocilib_impl.hpp:4741)\r\n\r\ntemplate<class T>\r\nBindObjectAdaptor<T>::BindObjectAdaptor(const Statement &statement, const ostring& name, unsigned int mode, ObjectType &object, unsigned int size) :\r\n     BindObject(statement, name, mode),\r\n     _object(object),\r\n     _data(new NativeType[size]),  <== here\r\n     _size(size)\r\n{\r\n    memset(_data, 0, _size * sizeof(NativeType));\r\n}\r\n==4428==    by 0x407CA7: void ocilib::Statement::Bind<std::vector<unsigned char, std::allocator<unsigned char> >, unsigned int>(std::string const&, std::vector<unsigned char, std::allocator<unsigned char> >&, unsigned int, ocilib::Enum<ocilib::BindInfo::BindDirectionValues>) (ocilib_impl.hpp:5391)\r\n==4428==    by 0x403809: testInsert(ocilib::Connection&, std::string&, std::vector<unsigned char, std::allocator<unsigned char> >&) (bugbind.cpp:29)\r\n==4428==    by 0x4043B5: main (bugbind.cpp:134)\r\n```\r\n\r\n### Passing an empty Raw\r\nExpected result is the same as passing an empty `ocilib::ostring`, instead an exception is thrown with internal error code 15 and error text `The integer parameter value must be at least 1`.\r\n\r\n\r\n## Test program\r\n\r\n```c++\r\n#include <iostream>\r\n#include <iomanip>\r\n\r\n#include \"ocilib.hpp\"\r\n\r\nusing namespace ocilib;\r\n\r\nvoid testInsert(Connection & con, ostring & name)\r\n{\r\n    Statement st(con);\r\n    st.Prepare(\"insert into testData(usr) values (:usr)\");\r\n    std::cout << \"Bind name \" << name << \", size=\" << name.size() << \", capacity=\" << name.capacity() << std::endl;\r\n    st.Bind<ostring, unsigned int>(\":usr\", name, static_cast<unsigned int>(name.size()), BindInfo::InOut);\r\n    st.ExecutePrepared();\r\n    con.Commit();\r\n}\r\n\r\nvoid testInsert(Connection & con, ostring & name, Raw & data)\r\n{\r\n    Statement st(con);\r\n    st.Prepare(\"insert into testData(usr, data) values (:usr, :data)\");\r\n    std::cout << \"Bind name \" << name << \", size=\" << name.size() << \", capacity=\" << name.capacity() << std::endl;\r\n    st.Bind<ostring, unsigned int>(\":usr\", name, 0, BindInfo::InOut);\r\n    std::cout << \"Bind data, size=\" << data.size() << \", capacity=\" << data.capacity() << std::endl;\r\n    st.Bind<ocilib::Raw, unsigned int>(\":data\", data, 0 /*data.size()+1*/, BindInfo::InOut);\r\n    st.ExecutePrepared();\r\n    con.Commit();\r\n}\r\n\r\nvoid dropTable(Connection & con)\r\n{\r\n    try\r\n    {\r\n       Statement st(con);\r\n       st.Execute(\"drop table testData\");\r\n    }\r\n    catch (std::exception & ex)\r\n    {\r\n        // ignore\r\n    }\r\n}\r\n\r\nvoid createTable(Connection & con)\r\n{\r\n    Statement st(con);\r\n    st.Execute(\"CREATE table testData( usr VARCHAR(32), data RAW(64) )\");\r\n}\r\n\r\nvoid testCon(Connection & con)\r\n{\r\n   Statement st(con);\r\n   st.Execute(\"SELECT COUNT(*) from USER_TABLES\");\r\n   Resultset rs = st.GetResultset();\r\n   if (rs.Next())\r\n   {\r\n      std::cout << \"User has \" << rs.Get<int>(1) << \" tables\"<< std::endl;\r\n   }\r\n}\r\n\r\nvoid dumpData(Connection & con)\r\n{\r\n   Statement st(con);\r\n   st.Execute(\"SELECT USR, DATA FROM testData\");\r\n   std::cout << st.GetSql() << std::endl;\r\n   Resultset rs = st.GetResultset();\r\n   while (rs.Next())\r\n   {\r\n      std::cout << rs.Get<ostring>(1) << \", \\t\";\r\n      if (rs.IsColumnNull(2))\r\n      {\r\n         std::cout << \"<null>\";\r\n      } else {\r\n         Raw raw = rs.Get<Raw>(2);\r\n         std::stringstream ss;\r\n         ss << std::hex << std::setfill('0');\r\n         for (const unsigned char & c : raw)\r\n         {\r\n            ss << std::setw(2) << static_cast<unsigned>(c) << \" \";\r\n         }\r\n         std::cout << ss.str();\r\n      }\r\n      std::cout << std::endl;\r\n   }\r\n}\r\n\r\nint main(int argc, char** argv)\r\n{\r\n    if (argc < 4)\r\n    {\r\n      std::cout << \"bugbind <dbcon> <user> <pwd>\" << std::endl;\r\n      return EXIT_FAILURE;\r\n    }\r\n\r\n    ostring connection = argv[1];\r\n    ostring user       = argv[2];\r\n    ostring pwd        = argv[3];\r\n    ostring home;\r\n    if (argc > 4)\r\n    {\r\n       home = argv[4];\r\n    }\r\n    try\r\n    {\r\n        Environment::Initialize(Environment::Default | Environment::Threaded, home);\r\n\r\n        Connection con;\r\n        con.Open(connection, user, pwd,  Environment::SessionDefault);\r\n        std::cout << \"Connecting to \" << user << \"/\" << pwd << \"@\" << connection << std::endl << std::endl;\r\n        std::cout << \"Connected to \" <<  con.GetServerMajorVersion()\r\n            << \".\" << con.GetServerMinorVersion()\r\n            << \".\" << con.GetServerRevisionVersion()\r\n            << \" runtime version \" << Environment::GetRuntimeVersion() << std::endl;\r\n        testCon(con);\r\n        dropTable(con);\r\n        std::cout << \"Table dropped\" << std::endl;\r\n        createTable(con);\r\n        std::cout << \"Table created\" << std::endl;\r\n        {\r\n           ostring user0(\"\");\r\n           testInsert(con, user0);\r\n        }\r\n        {\r\n           ostring user1(\"John\");\r\n           std::vector<unsigned char> data;\r\n           data.resize(3);\r\n           data.reserve(15);\r\n           data[0] = 0;\r\n           data[1] = 1;\r\n           data[2] = 2;\r\n           testInsert(con, user1,  data);\r\n        }\r\n        {\r\n           ostring user2(\"Joe\");\r\n           std::vector<unsigned char> data;\r\n           data.resize(5);\r\n           data[0] = 0;\r\n           data[1] = 1;\r\n           data[2] = 2;\r\n           data[3] = 3;\r\n           data[4] = 4;\r\n           testInsert(con, user2, data);\r\n        }\r\n        dumpData(con);\r\n        con.Commit();\r\n        {\r\n           ostring user2(\"Crash\");\r\n           std::vector<unsigned char> data;\r\n           testInsert(con, user2, data);\r\n        }\r\n        con.Commit();\r\n    }\r\n    catch (Exception & ex)\r\n    {\r\n       std::cout << ex.what() << std::endl;\r\n       std::cout << ex.GetMessage() << std::endl;\r\n       std::cout << \"Internal error code: \" << ex.GetInternalErrorCode() << std::endl;\r\n       std::cout << \"Oracle error code:   \" << ex.GetOracleErrorCode() << std::endl;\r\n    }\r\n    catch (std::exception &ex)\r\n    {\r\n        std::cout << ex.what() << std::endl;\r\n    }\r\n\r\n    Environment::Cleanup();\r\n\r\n    return EXIT_SUCCESS;\r\n}\r\n```\r\n\r\n## Possible fix\r\nProbably write a specialized adapter for `Raw ` \r\nor allocate another element like (downside is that all the working bind will allocate another element).\r\n```c++\r\nstd::vector<unsigned char, std::allocator<unsigned char> >&, unsigned int) (ocilib_impl.hpp:4741)\r\ntemplate<class T>\r\nBindObjectAdaptor<T>::BindObjectAdaptor(const Statement &statement, const ostring& name, unsigned int mode, ObjectType &object, unsigned int size) :\r\n     BindObject(statement, name, mode),\r\n     _object(object),\r\n     _data(new NativeType[size + 1]), \r\n     _size(size)\r\n{\r\n    memset(_data, 0, _size * sizeof(NativeType));\r\n}\r\n```"}}
{"actor":{"display_login":"kornefalk"}}