diff --git a/.clang-tidy b/.clang-tidy index 7d343ead..d42139e3 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,6 +1,7 @@ --- Checks: -'clang-diagnostic-*,clang-analyzer-*,performance-*,readability-*,modernize-*,bugprone-*,misc-*,-modernize-use-trailing-return-type' +'clang-diagnostic-*,clang-analyzer-*,performance-*,readability-*,modernize-*,bugprone-*,misc-*, +-modernize-use-trailing-return-type,-bugprone-easily-swappable-parameters,-readability-identifier-length' WarningsAsErrors: '*' HeaderFilterRegex: 'include/aws/.*\.h$' FormatStyle: 'none' diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml new file mode 100644 index 00000000..495f6a6a --- /dev/null +++ b/.github/workflows/code-quality.yml @@ -0,0 +1,58 @@ +name: "Code Quality" + +on: + push: + branches: [ "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + - cron: '32 14 * * 2' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'cpp' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Install libcurl dependency + - name: Install dependencies + run: sudo apt-get update && sudo apt install -y libcurl4-openssl-dev + + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + - run: | + echo "Run, CMake build script" + cmake -B ${{github.workspace}}/build -DBUILD_SHARED_LIBS=ON -DBUILD_TESTING=OFF + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" + diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 14bb1dac..526b38a9 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Dependencies run: sudo apt-get update && sudo apt-get install -y clang-tidy libcurl4-openssl-dev @@ -36,7 +36,7 @@ jobs: build-on-arm-too: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: uraimo/run-on-arch-action@v2 with: arch: aarch64 @@ -46,13 +46,35 @@ jobs: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_CXX_CLANG_TIDY=clang-tidy cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} + build-demo: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Install Dependencies + run: sudo apt-get update && sudo apt-get install -y clang-tidy libcurl4-openssl-dev + + - name: Build and install lambda runtime + run: | + mkdir build && cd build + cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=~/lambda-install + make + make install + + - name: Build and package demo project + run: | + cd examples/demo + mkdir build && cd build + cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=~/lambda-install + make + make aws-lambda-package-demo format: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Check Formatting run: ./ci/codebuild/format-check.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 76702cb5..09e226fd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ project(aws-lambda-runtime option(ENABLE_LTO "Enables link-time optimization, requires compiler support." OFF) option(ENABLE_TESTS "Enables building the test project, requires AWS C++ SDK." OFF) +option(ENABLE_SANITIZERS "Enables ASan and UBSan." OFF) add_library(${PROJECT_NAME} "src/logging.cpp" @@ -22,6 +23,11 @@ target_include_directories(${PROJECT_NAME} PUBLIC $ $) +if (ENABLE_SANITIZERS) + target_compile_options(${PROJECT_NAME} PUBLIC "-fsanitize=address,undefined") + target_link_libraries(${PROJECT_NAME} PUBLIC "-fsanitize=address,undefined") +endif() + if (ENABLE_LTO) include(CheckIPOSupported) check_ipo_supported(RESULT has_lto OUTPUT lto_check_output) @@ -41,8 +47,29 @@ endif() target_include_directories(${PROJECT_NAME} PRIVATE ${CURL_INCLUDE_DIRS}) -find_package(Backtrace REQUIRED) -target_link_libraries(${PROJECT_NAME} PRIVATE ${Backtrace_LIBRARIES}) +find_package(Backtrace QUIET) +if (${Backtrace_FOUND}) + target_link_libraries(${PROJECT_NAME} PRIVATE ${Backtrace_LIBRARIES}) + + find_library(DW_LIB NAMES dw) + if (NOT DW_LIB STREQUAL DW_LIB-NOTFOUND) + message("-- Enhanced stack-traces are enabled via libdw: ${DW_LIB}") + target_compile_definitions(${PROJECT_NAME} PRIVATE "BACKWARD_HAS_DW=1") + target_link_libraries(${PROJECT_NAME} PUBLIC "${DW_LIB}") + else() + find_library(BFD_LIB NAMES bfd) + if (NOT BFD_LIB STREQUAL BFD_LIB-NOTFOUND) + message("-- Enhanced stack-traces are enabled via libbfd: ${BFD_LIB}") + target_compile_definitions(${PROJECT_NAME} PRIVATE "BACKWARD_HAS_BFD=1") + target_link_libraries(${PROJECT_NAME} PRIVATE "${BFD_LIB}") + endif() + endif() + +else() + message("-- libbacktrace was not installed. Stacktracing will be disabled") + add_definitions(-Dno_backtrace) +endif() + target_compile_options(${PROJECT_NAME} PRIVATE "-fno-exceptions" @@ -55,20 +82,6 @@ target_compile_options(${PROJECT_NAME} PRIVATE "-Wconversion" "-Wno-sign-conversion") -find_library(DW_LIB NAMES dw) -if (NOT DW_LIB STREQUAL DW_LIB-NOTFOUND) - message("-- Enhanced stack-traces are enabled via libdw: ${DW_LIB}") - target_compile_definitions(${PROJECT_NAME} PRIVATE "BACKWARD_HAS_DW=1") - target_link_libraries(${PROJECT_NAME} PUBLIC "${DW_LIB}") -else() - find_library(BFD_LIB NAMES bfd) - if (NOT BFD_LIB STREQUAL BFD_LIB-NOTFOUND) - message("-- Enhanced stack-traces are enabled via libbfd: ${BFD_LIB}") - target_compile_definitions(${PROJECT_NAME} PRIVATE "BACKWARD_HAS_BFD=1") - target_link_libraries(${PROJECT_NAME} PRIVATE "${BFD_LIB}") - endif() -endif() - if (LOG_VERBOSITY) target_compile_definitions(${PROJECT_NAME} PRIVATE "AWS_LAMBDA_LOG=${LOG_VERBOSITY}") elseif(CMAKE_BUILD_TYPE STREQUAL Debug) diff --git a/README.md b/README.md index f86004a9..74e60af6 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ [![GitHub](https://img.shields.io/github/license/awslabs/aws-lambda-cpp.svg)](https://github.com/awslabs/aws-lambda-cpp/blob/master/LICENSE) -[![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/awslabs/aws-lambda-cpp.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/awslabs/aws-lambda-cpp/context:cpp) +![Code Quality badge](https://github.com/awslabs/aws-lambda-cpp/actions/workflows/code-quality.yml/badge.svg) | OS | Arch | Status | |----|------|--------| diff --git a/ci/codebuild/arch-linux.yml b/ci/codebuild/arch-linux.yml index 4262f502..0a12a5d1 100644 --- a/ci/codebuild/arch-linux.yml +++ b/ci/codebuild/arch-linux.yml @@ -4,6 +4,6 @@ phases: build: commands: - echo Build started on `date` - - ./ci/codebuild/build.sh -DTEST_RESOURCE_PREFIX=lambda-cpp-archbtw + - ./ci/codebuild/build.sh -DTEST_RESOURCE_PREFIX=lambda-cpp-archbtw -DENABLE_SANITIZERS=ON - ./ci/codebuild/run-tests.sh aws-lambda-package-lambda-test-fun - echo Build completed on `date` diff --git a/ci/codebuild/format-check.sh b/ci/codebuild/format-check.sh index 222b784f..035bd54f 100755 --- a/ci/codebuild/format-check.sh +++ b/ci/codebuild/format-check.sh @@ -13,6 +13,11 @@ FAIL=0 SOURCE_FILES=$(find src include tests -type f -name "*.h" -o -name "*.cpp") for i in $SOURCE_FILES do + if [[ "$i" == *"gtest.h" || "$i" == *"backward.h" ]]; then + continue + fi + + echo "$i\n" if [ $($CLANG_FORMAT -output-replacements-xml $i | grep -c " + +using namespace aws::lambda_runtime; + +static invocation_response my_handler(invocation_request const& req) +{ + if (req.payload.length() > 42) { + return invocation_response::failure("error message here"/*error_message*/, + "error type here" /*error_type*/); + } + + return invocation_response::success("json payload here" /*payload*/, + "application/json" /*MIME type*/); +} + +int main() +{ + run_handler(my_handler); + return 0; +} diff --git a/examples/dynamodb/main.cpp b/examples/dynamodb/main.cpp index a8b86621..6089fa32 100644 --- a/examples/dynamodb/main.cpp +++ b/examples/dynamodb/main.cpp @@ -173,9 +173,9 @@ aws::lambda_runtime::invocation_response my_handler( if (cr.error_msg) { JsonValue response; response.WithString("body", cr.error_msg).WithInteger("statusCode", 400); - auto const apig_response = response.View().WriteCompact(); + auto apig_response = response.View().WriteCompact(); AWS_LOGSTREAM_ERROR(TAG, "Validation failed. " << apig_response); - return aws::lambda_runtime::invocation_response::success(apig_response, "application/json"); + return aws::lambda_runtime::invocation_response::success(std::move(apig_response), "application/json"); } auto result = query(cr, client); @@ -190,10 +190,10 @@ aws::lambda_runtime::invocation_response my_handler( response.WithString("body", "No data found for this product.").WithInteger("statusCode", 400); } - auto const apig_response = response.View().WriteCompact(); + auto apig_response = response.View().WriteCompact(); AWS_LOGSTREAM_DEBUG(TAG, "api gateway response: " << apig_response); - return aws::lambda_runtime::invocation_response::success(apig_response, "application/json"); + return aws::lambda_runtime::invocation_response::success(std::move(apig_response), "application/json"); } std::function()> GetConsoleLoggerFactory() diff --git a/examples/s3/main.cpp b/examples/s3/main.cpp index 45b935bf..892560cb 100644 --- a/examples/s3/main.cpp +++ b/examples/s3/main.cpp @@ -50,7 +50,7 @@ static invocation_response my_handler(invocation_request const& req, Aws::S3::S3 return invocation_response::failure(err, "DownloadFailure"); } - return invocation_response::success(base64_encoded_file, "application/base64"); + return invocation_response::success(std::move(base64_encoded_file), "application/base64"); } std::function()> GetConsoleLoggerFactory() @@ -73,8 +73,7 @@ int main() config.region = Aws::Environment::GetEnv("AWS_REGION"); config.caFile = "/etc/pki/tls/certs/ca-bundle.crt"; - auto credentialsProvider = Aws::MakeShared(TAG); - S3::S3Client client(credentialsProvider, config); + S3::S3Client client(config); auto handler_fn = [&client](aws::lambda_runtime::invocation_request const& req) { return my_handler(req, client); }; diff --git a/include/aws/lambda-runtime/runtime.h b/include/aws/lambda-runtime/runtime.h index 96023697..46b88171 100644 --- a/include/aws/lambda-runtime/runtime.h +++ b/include/aws/lambda-runtime/runtime.h @@ -105,7 +105,7 @@ class invocation_response { /** * Create a successful invocation response with the given payload and content-type. */ - static invocation_response success(std::string const& payload, std::string const& content_type); + static invocation_response success(std::string payload, std::string content_type); /** * Create a failure response with the given error message and error type. @@ -129,8 +129,7 @@ class invocation_response { bool is_success() const { return m_success; } }; -struct no_result { -}; +struct no_result {}; class runtime { public: diff --git a/packaging/packager b/packaging/packager index 4e8759ef..0c4e7493 100755 --- a/packaging/packager +++ b/packaging/packager @@ -56,7 +56,7 @@ if ! type zip > /dev/null 2>&1; then exit 1 fi -function find_so_files() { +function pluck_so_files() { sed -E '/\.so$|\.so\.[0-9]+$/!d' } @@ -64,7 +64,7 @@ function package_libc_alpine() { # -F matches a fixed string rather than a regex (grep that comes with busybox doesn't know --fixed-strings) if grep -F "Alpine Linux" < /etc/os-release > /dev/null; then if type apk > /dev/null 2>&1; then - apk info --contents musl 2>/dev/null | find_so_files | sed 's/^/\//' + apk info --contents musl 2>/dev/null | pluck_so_files | sed 's/^/\//' fi fi } @@ -72,20 +72,27 @@ function package_libc_alpine() { function package_libc_pacman() { if grep --extended-regexp "Arch Linux|Manjaro Linux" < /etc/os-release > /dev/null 2>&1; then if type pacman > /dev/null 2>&1; then - pacman --query --list --quiet glibc | find_so_files + pacman --query --list --quiet glibc | pluck_so_files fi fi } function package_libc_dpkg() { if type dpkg-query > /dev/null 2>&1; then - dpkg-query --listfiles libc6:$(dpkg --print-architecture) | find_so_files + architecture=$(dpkg --print-architecture) + if [[ $(dpkg-query --listfiles libc6:$architecture | wc -l) -gt 0 ]]; then + dpkg-query --listfiles libc6:$architecture | pluck_so_files + fi fi } function package_libc_rpm() { + arch=$(uname -m) + if type rpm > /dev/null 2>&1; then - rpm --query --list glibc.$(uname -m) | find_so_files + if [[ $(rpm --query --list glibc.$arch | wc -l) -gt 1 ]]; then + rpm --query --list glibc.$arch | pluck_so_files + fi fi } diff --git a/src/backward.cpp b/src/backward.cpp index cc64abdb..8649fd51 100644 --- a/src/backward.cpp +++ b/src/backward.cpp @@ -23,10 +23,14 @@ // - g++/clang++ -lbfd ... // #define BACKWARD_HAS_BFD 1 -#include "backward.h" +#ifndef no_backtrace + +# include "backward.h" namespace backward { backward::SignalHandling sh; } // namespace backward + +#endif diff --git a/src/backward.h b/src/backward.h index c4213789..832c4528 100644 --- a/src/backward.h +++ b/src/backward.h @@ -28,6 +28,10 @@ # error "It's not going to compile without a C++ compiler..." #endif +#ifdef no_backtrace +# pragma message "Disabling stacktracing" +#else + #if defined(BACKWARD_CXX11) #elif defined(BACKWARD_CXX98) #else @@ -4539,4 +4543,6 @@ class SignalHandling { } // namespace backward +#endif /* no_backtrace */ + #endif /* H_GUARD */ diff --git a/src/runtime.cpp b/src/runtime.cpp index 47ead0e7..2ae91e29 100644 --- a/src/runtime.cpp +++ b/src/runtime.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include // for strtoul #include @@ -133,12 +134,16 @@ static size_t read_data(char* buffer, size_t size, size_t nitems, void* userdata } if (unread <= limit) { - std::copy_n(ctx->first.begin() + ctx->second, unread, buffer); + auto from = ctx->first.begin(); + std::advance(from, ctx->second); + std::copy_n(from, unread, buffer); ctx->second += unread; return unread; } - std::copy_n(ctx->first.begin() + ctx->second, limit, buffer); + auto from = ctx->first.begin(); + std::advance(from, ctx->second); + std::copy_n(from, limit, buffer); ctx->second += limit; return limit; } @@ -496,7 +501,7 @@ static std::string json_escape(std::string const& in) // escape and print as unicode codepoint constexpr int printed_unicode_length = 6; // 4 hex + letter 'u' + \0 std::array buf; - sprintf(buf.data(), "u%04x", ch); + snprintf(buf.data(), buf.size(), "u%04x", ch); out.append(buf.data(), buf.size() - 1); // add only five, discarding the null terminator. break; } @@ -506,12 +511,12 @@ static std::string json_escape(std::string const& in) } AWS_LAMBDA_RUNTIME_API -invocation_response invocation_response::success(std::string const& payload, std::string const& content_type) +invocation_response invocation_response::success(std::string payload, std::string content_type) { invocation_response r; r.m_success = true; - r.m_content_type = content_type; - r.m_payload = payload; + r.m_content_type = std::move(content_type); + r.m_payload = std::move(payload); return r; } diff --git a/tests/resources/lambda_function.cpp b/tests/resources/lambda_function.cpp index 7cd62e55..b0228d3c 100644 --- a/tests/resources/lambda_function.cpp +++ b/tests/resources/lambda_function.cpp @@ -23,8 +23,8 @@ invocation_response echo_failure(invocation_request const& /*request*/) invocation_response binary_response(invocation_request const& /*request*/) { - const std::string png((char*)awslogo_png, AWSLOGO_PNG_LEN); - return invocation_response::success(png, "image/png"); + std::string png((char*)awslogo_png, AWSLOGO_PNG_LEN); + return invocation_response::success(std::move(png), "image/png"); } invocation_response crash_backtrace(invocation_request const& /*request*/)