From 05b2e6dd5e3c08a5d11fa7a46f3ed8f555ff9a7f Mon Sep 17 00:00:00 2001 From: DavidKorczynski Date: Tue, 30 Aug 2022 10:35:05 +0100 Subject: [PATCH] pyodbc: initial integration (#8347) * pyodbc: initial integration * set up correct types in odbc driver --- projects/pyodbc/Dockerfile | 21 ++++++++++ projects/pyodbc/build.sh | 28 ++++++++++++++ projects/pyodbc/fake_odbc_driver.c | 46 ++++++++++++++++++++++ projects/pyodbc/fuzz_curs_exec.py | 61 ++++++++++++++++++++++++++++++ projects/pyodbc/project.yaml | 11 ++++++ 5 files changed, 167 insertions(+) create mode 100644 projects/pyodbc/Dockerfile create mode 100755 projects/pyodbc/build.sh create mode 100644 projects/pyodbc/fake_odbc_driver.c create mode 100644 projects/pyodbc/fuzz_curs_exec.py create mode 100644 projects/pyodbc/project.yaml diff --git a/projects/pyodbc/Dockerfile b/projects/pyodbc/Dockerfile new file mode 100644 index 000000000..63e015f32 --- /dev/null +++ b/projects/pyodbc/Dockerfile @@ -0,0 +1,21 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +################################################################################ + +FROM gcr.io/oss-fuzz-base/base-builder-python +RUN apt-get update && apt-get install -y make autoconf automake libtool unixodbc-dev +RUN git clone --depth 1 https://github.com/mkleehammer/pyodbc +WORKDIR pyodbc +COPY build.sh *.py *.c $SRC/ diff --git a/projects/pyodbc/build.sh b/projects/pyodbc/build.sh new file mode 100755 index 000000000..9d277caa5 --- /dev/null +++ b/projects/pyodbc/build.sh @@ -0,0 +1,28 @@ +#!/bin/bash -eu +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +################################################################################ + +# Compile the fake odbc driver +clang -Wno-unused-result -Wsign-compare -Wunreachable-code \ + -fwrapv -Wno-write-strings -fPIC \ + -shared -I/usr/local/include/python3.8 -I$PWD/src \ + -o $OUT/fuzzodbc.so $SRC/fake_odbc_driver.c + +python3 setup.py install +pip3 install . +for fuzzer in $(find $SRC -name 'fuzz_*.py'); do + LD_PRELOAD=$OUT/sanitizer_with_fuzzer.so ASAN_OPTIONS=detect_leaks=0 compile_python_fuzzer $fuzzer --add-data $OUT/fuzzodbc.so:. +done diff --git a/projects/pyodbc/fake_odbc_driver.c b/projects/pyodbc/fake_odbc_driver.c new file mode 100644 index 000000000..8552185e8 --- /dev/null +++ b/projects/pyodbc/fake_odbc_driver.c @@ -0,0 +1,46 @@ +/* Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +/* Fake odbc driver that simply returns 0 on every call and has no side effects */ + +#include +#include + +SQLRETURN SQLAllocHandle(SQLSMALLINT a1, SQLHANDLE a2, SQLHANDLE *a3) { + return 0; +} + +SQLRETURN +SQLDriverConnect(SQLHDBC ConnectionHandle, SQLHWND WindowHandle, + SQLCHAR *InConnectionString, SQLSMALLINT StringLength1, + SQLCHAR *OutConnectionString, SQLSMALLINT BufferLength, + SQLSMALLINT *StringLength2Ptr, SQLUSMALLINT DriverCompletion) { + return 0; +} + +SQLRETURN SQLSetConnectAttr(SQLHDBC ConnectionHandle, SQLINTEGER Attribute, + SQLPOINTER ValuePtr, SQLINTEGER StringLength) { + return 0; +} + +SQLRETURN SQL_API SQLExecDirectW(SQLHSTMT hstmt, SQLWCHAR *szSqlStr, + SQLINTEGER cbSqlStr) { + return 0; +} + +SQLRETURN SQL_API SQLRowCount(SQLHSTMT StatementHandle, SQLLEN *RowCount) { + return 0; +} + +SQLRETURN SQL_API SQLNumResultCols(SQLHSTMT StatementHandle, + SQLSMALLINT *ColumnCount) { + return 0; +} diff --git a/projects/pyodbc/fuzz_curs_exec.py b/projects/pyodbc/fuzz_curs_exec.py new file mode 100644 index 000000000..324c6d082 --- /dev/null +++ b/projects/pyodbc/fuzz_curs_exec.py @@ -0,0 +1,61 @@ +#!/usr/bin/python3 +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Fuzzer that targets native code of pyodbc with a fake odbc driver""" +import os +import sys +import atheris +import pyodbc + +@atheris.instrument_func +def fuzz_exec(data): + fdp = atheris.FuzzedDataProvider(data) + connstr = "DRIVER=FUZZ;" + s1 = fdp.ConsumeUnicodeNoSurrogates(50) + if "DRIVER" in s1: + return + cstr = connstr + s1 + connection_obj = pyodbc.connect(cstr) + csr = connection_obj.cursor() + try: + csr.execute(fdp.ConsumeUnicodeNoSurrogates(20)) + except Exception as e: + if "Invalid string or buffer length" in str(e): + pass + else: + raise e + return 0 + +@atheris.instrument_func +def TestOneInput(data): + try: + return fuzz_exec(data) + except SystemError: + return 0 + + +def main(): + # Write the odbcinst.ini file + dir_path = os.path.dirname(os.path.realpath(__file__)) + with open("/etc/odbcinst.ini", "w") as f: + f.write("[FUZZ]\n") + f.write("Driver=%s/fuzzodbc.so\n"%(dir_path)) + + atheris.instrument_all() + atheris.Setup(sys.argv, TestOneInput, enable_python_coverage=True) + atheris.Fuzz() + + +if __name__ == "__main__": + main() diff --git a/projects/pyodbc/project.yaml b/projects/pyodbc/project.yaml new file mode 100644 index 000000000..4019972c2 --- /dev/null +++ b/projects/pyodbc/project.yaml @@ -0,0 +1,11 @@ +homepage: "https://github.com/mkleehammer/pyodbc" +language: python +main_repo: "https://github.com/mkleehammer/pyodbc" +fuzzing_engines: +- libfuzzer +sanitizers: +- address +- undefined +vendor_ccs: +- david@adalogics.com +- adam@adalogics.com