chromium/third_party/sqlite/BUILD.gn

# Copyright 2013 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import("//build/config/dcheck_always_on.gni")
import("//build/config/sanitizers/sanitizers.gni")
import("//testing/libfuzzer/fuzzer_test.gni")
import("//third_party/protobuf/proto_library.gni")
import("//third_party/sqlite/sqlite_chromium_configuration_flags.gni")
import("//third_party/sqlite/sqlite_common_configuration_flags.gni")
import("//third_party/sqlite/sqlite_dev_configuration_flags.gni")

# Compile-time options passed to SQLite.
#
# These options are used when building our own SQLite library, which happens
# everywhere except on iOS. These compile-time options are exported via a
# public_config to all targets using SQLite, because they're needed by the
# sqlite.h header. To avoid name clashes (macro names are resolved using a
# global namespace), this block should only contain preprocessor macros that
# are unambiguously connected to SQLite.
#
# The vast majority of the macros here are documented at
# https://www.sqlite.org/compile.html
config("common_sqlite3_compile_options") {
  # Start with the configuration flags which are also baked into the SQLite
  # source when the amalgamation is generated. These are the same for all
  # platforms.
  defines = sqlite_common_configuration_flags

  # The flags not baked in, but common for all platforms.
  defines += [
    # Skip writing transaction rollback journals on f2fs.
    # f2fs tends to be used on Android, and may be used on ChromeOS.
    "SQLITE_ENABLE_BATCH_ATOMIC_WRITE",

    # Forces SQLite to only store temporary data in memory.
    #
    # This is the only supported setting on Android. Using it everywhere removes
    # a source of cross-platform variation, and saves us from having to reason
    # about SQLite storing data in the operating system's temporary directory.
    "SQLITE_TEMP_STORE=3",
  ]

  # On OSX, SQLite has extra logic for detecting the use of network
  # filesystems (e.g., AFS, NFS) and for working around locking problems in
  # these filesystems. This logic is gated by SQLITE_ENABLE_LOCKING_STYLE, which
  # is 1 by default on OSX and iOS, and 0 everywhere else.
  #
  # When enabled, SQLITE_ENABLE_LOCKING_STYLE results in a compile-time warning
  # on iOS. The recommended solution is to disable the flag on iOS, because
  # iOS doesn't (yet?) have networked filesystems. Since we have to do this,
  # might as well be explicit about the flag everywhere.
  if (is_mac) {
    defines += [ "SQLITE_ENABLE_LOCKING_STYLE=1" ]
  } else {
    defines += [ "SQLITE_ENABLE_LOCKING_STYLE=0" ]
  }

  if (using_sanitizer) {
    defines += [
      # Limit max length of data blobs and queries to 128 MB for fuzzing builds.
      "SQLITE_MAX_LENGTH=128000000",
      "SQLITE_MAX_SQL_LENGTH=128000000",
      "SQLITE_PRINTF_PRECISION_LIMIT=1280000",
    ]

    # During fuzz testing, valid SQL queries generated by fuzzing engine may
    # lead to large memory allocations. If that happens, fuzzer reports an
    # out-of-memory error. However, such errors are not valid bugs.
    # To avoid hitting those irrelevant OOMs, we limit max number of memory
    # pages, so fuzzer will not crash when reaching the limit.
    # Apply this for fuzzing builds only, not for all builds with sanitizers.
    if (use_fuzzing_engine) {
      defines += [
        "SQLITE_MAX_PAGE_COUNT=16384",

        # Used to deserialize a database from a libfuzzer-provided data blob.
        # This is to fuzz SQLite's resilience to database corruption.
        "SQLITE_ENABLE_DESERIALIZE",
      ]

      # The progress callback is used in fuzzing to cancel long-running queries
      # so we don't spend too much time on them.
      defines -= [ "SQLITE_OMIT_PROGRESS_CALLBACK" ]
    }
  }

  if (is_debug || dcheck_always_on) {
    if (use_fuzzing_engine && use_sanitizer_coverage) {
      # Enable SQLite's assert() macros.
      #
      # TODO(pwnall): Fix all the bugs preventing us from enabling this flag for
      #               all DCHECK builds. See https://crbug.com/907371 and
      #               https://crrev.com/c/1343529.
      defines += [ "SQLITE_DEBUG" ]
    }

    # Check preconditions when SQLite APIs are called. See
    # https://sqlite.org/compile.html#enable_api_armor
    #
    # fuzzing builds have this disabled because the fuzzers are guaranteed to
    # use the API correctly, and removing the checks opens up the possibility
    # that the fuzzers will get more code coverage.
    defines += [ "SQLITE_ENABLE_API_ARMOR" ]
  }
}

# These options add SQLite features that we need in Chrome.
config("chromium_sqlite3_compile_options") {
  configs = [ ":common_sqlite3_compile_options" ]

  defines = sqlite_chromium_configuration_flags
}

# These options add SQLite features that we need in developer tools.
config("dev_sqlite3_compile_options") {
  configs = [ ":common_sqlite3_compile_options" ]

  defines = sqlite_dev_configuration_flags
}

config("sqlite_warnings") {
  cflags = []
  if (is_clang) {
    cflags += [
      "-Wno-shadow",

      # sqlite contains a few functions that are unused, at least on
      # Windows with Chrome's sqlite patches applied
      # (interiorCursorEOF fts3EvalDeferredPhrase
      # fts3EvalSelectDeferred sqlite3Fts3InitHashTable
      # sqlite3Fts3InitTok).
      "-Wno-unused-function",
    ]

    if (is_debug || dcheck_always_on) {
      cflags += [
        # SQLite uses assert(!"description") to express
        # NOT_REACHED() << "description". This is considered an implicit
        # conversion from char[] to bool, and triggers a warning.
        "-Wno-string-conversion",
      ]
    }
  }
  if (is_linux || is_chromeos) {
    cflags += [
      # SQLite doesn't believe in compiler warnings, preferring testing.
      # http://www.sqlite.org/faq.html#q17
      "-Wno-int-to-pointer-cast",
      "-Wno-pointer-to-int-cast",
    ]
  }
  if (is_ios) {
    cflags += [
      # SQLite issues a #pragma warning on iOS.
      # http://sqlite.1065341.n5.nabble.com/Compiler-warning-quot-gethostuuid-is-disabled-quot-building-SQLite-for-iOS-td96881.html
      #
      # Contrary to what is said on the mailing list, setting
      # SQLITE_ENABLE_LOCKING_STYLE to 0 does not make the warning go away.
      "-Wno-#warnings",
    ]
  }
  if (is_win && !is_clang) {
    cflags += [ "/wd4101" ]  # 'zTrace' unreferenced variable in src/src/vdbe.c
  }
}

# Naming the library "sqlite3" can cause conflicts with the system library.
component("chromium_sqlite3") {
  visibility = [ ":*" ]

  public = [ "sqlite3.h" ]

  sources = [
    "sqlite3_shim.c",
    "sqlite3_shim_fixups.h",
    "src/amalgamation/rename_exports.h",
    "src/amalgamation/sqlite3.h",
  ]

  inputs = [
    # This file is #included into sqlite3_shim.c, which injects Chrome-specific
    # definitions into the SQLite amalgamation code.
    "src/amalgamation/sqlite3.c",
  ]

  cflags = []
  defines = []

  if (is_component_build) {
    if (is_win) {
      defines += [ "SQLITE_API=__declspec(dllexport)" ]
    } else {
      defines += [ "SQLITE_API=__attribute__((visibility(\"default\")))" ]
    }
  }

  if (is_linux || is_chromeos || is_android) {
    defines += [
      # Linux provides fdatasync(), a faster equivalent of fsync().
      "fdatasync=fdatasync",
    ]
  }

  if (is_posix || is_fuchsia) {
    defines += [
      # Allow xSleep() call on Unix to use usleep() rather than sleep(), so it
      # will have microsecond precision.  Should only affect contended
      # databases via the busy callback.  Browser profile databases are mostly
      # exclusive, but renderer databases may allow for contention.
      "HAVE_USLEEP=1",

      # Use pread/pwrite directly rather than emulating them.
      "USE_PREAD=1",
    ]
  }

  include_dirs = [
    ".",  # sqlite3.h here must override the one in src/amalgamation/.
    "src/amalgamation",
  ]

  configs -= [ "//build/config/compiler:chromium_code" ]
  configs += [
    ":chromium_sqlite3_compile_options",
    "//build/config/compiler:no_chromium_code",
    "//build/config/sanitizers:cfi_icall_generalize_pointers",

    # Must be after no_chromium_code for warning flags to be ordered correctly.
    ":sqlite_warnings",
  ]

  if (is_apple) {
    frameworks = [ "CoreFoundation.framework" ]
    if (!is_ios) {
      frameworks += [ "CoreServices.framework" ]
    }
  } else if (is_android) {
    defines += [
      "SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT=1048576",
      "SQLITE_DEFAULT_AUTOVACUUM=1",
    ]
  }

  deps = [ "//third_party/icu" ]
}

# Similar to component("chromium_sqlite") above, but uses the developer tools
# options at config("dev_sqlite3_compile_options").
#
# This target uses source_set instead of component intentionally because turning
# it into a shared library will result in symbol collisions with "chromium_sqlite".
# Given that this config is effectively test-only, we should link it statically
# and allow "chromium_sqlite" to be the only shared library.
# TODO(altimin): mark this component as testonly = true.
source_set("dev_sqlite3") {
  visibility = [ ":*" ]

  public = [ "dev/sqlite3.h" ]

  sources = [
    "dev/sqlite3_shim.c",
    "sqlite3_shim_fixups.h",
    "src/amalgamation_dev/rename_exports.h",
    "src/amalgamation_dev/sqlite3.h",
  ]

  inputs = [
    # This file is #included into sqlite3_shim.c, which injects Chrome-specific
    # definitions into the SQLite amalgamation code.
    "src/amalgamation_dev/sqlite3.c",
  ]

  cflags = []
  defines = []

  if (is_linux || is_chromeos || is_android) {
    defines += [
      # Linux provides fdatasync(), a faster equivalent of fsync().
      "fdatasync=fdatasync",
    ]
  }

  if (is_posix || is_fuchsia) {
    defines += [
      # Allow xSleep() call on Unix to use usleep() rather than sleep(), so it
      # will have microsecond precision.  Should only affect contended
      # databases via the busy callback.  Browser profile databases are mostly
      # exclusive, but renderer databases may allow for contention.
      "HAVE_USLEEP=1",

      # Use pread/pwrite directly rather than emulating them.
      "USE_PREAD=1",
    ]
  }

  include_dirs = [
    "dev",  # sqlite3.h here must override the one in amalgamation_dev/.
    "src/amalgamation_dev",
  ]

  configs -= [ "//build/config/compiler:chromium_code" ]
  configs += [
    ":dev_sqlite3_compile_options",
    "//build/config/compiler:no_chromium_code",
    "//build/config/sanitizers:cfi_icall_generalize_pointers",

    # Must be after no_chromium_code for warning flags to be ordered correctly.
    ":sqlite_warnings",
  ]

  if (is_apple) {
    frameworks = [ "CoreFoundation.framework" ]
    if (!is_ios) {
      frameworks += [ "CoreServices.framework" ]
    }
  } else if (is_android) {
    defines += [
      "SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT=1048576",
      "SQLITE_DEFAULT_AUTOVACUUM=1",
    ]
  }

  deps = [ "//third_party/icu" ]
}

config("sqlite_export") {
  if (is_component_build && is_win) {
    defines = [ "SQLITE_API=__declspec(dllimport)" ]
  }
}

# This is used to allow the SQLITE_API definition to be different when
# building sqlite3.c than it is when clients include sqlite3.h.
group("sqlite") {
  public_deps = [ ":chromium_sqlite3" ]
  public_configs = [
    ":chromium_sqlite3_compile_options",
    ":sqlite_export",
  ]
}

group("sqlite_dev") {
  public_deps = [ ":dev_sqlite3" ]
  public_configs = [ ":dev_sqlite3_compile_options" ]
}

if (is_win || is_mac || is_linux || is_chromeos) {
  executable("sqlite_shell") {
    include_dirs = [
      # SQLite's shell.c contains an '#include "sqlite3.h", which we want to be
      # resolved to //third_party/sqlite/sqlite3.h.
      ".",
    ]

    sources = [
      "sqlite_shell_icu_helper.cc",
      "sqlite_shell_icu_helper.h",
      "sqlite_shell_shim.c",
    ]

    deps = [
      ":sqlite",
      "//base",
      "//base:i18n",
      "//third_party/icu",
    ]

    configs -= [ "//build/config/compiler:chromium_code" ]
    configs += [
      ":chromium_sqlite3_compile_options",
      "//build/config/compiler:no_chromium_code",

      # Must be after no_chromium_code for warning flags to be ordered
      # correctly.
      ":sqlite_warnings",
    ]
  }

  # Similar to executable("sqlite_shell") above, but uses the developer tools
  # options at config("dev_sqlite3_compile_options") and group("sqlite_dev").
  executable("sqlite_dev_shell") {
    include_dirs = [
      # SQLite's shell.c contains an '#include "sqlite3.h", which we want to be
      # resolved to //third_party/sqlite/sqlite3.h.
      ".",
    ]

    sources = [
      "sqlite_shell_icu_helper.cc",
      "sqlite_shell_icu_helper.h",
      "sqlite_shell_shim.c",
    ]

    deps = [
      ":sqlite_dev",
      "//base",
      "//base:i18n",
      "//third_party/icu",
    ]

    configs -= [ "//build/config/compiler:chromium_code" ]
    configs += [
      ":dev_sqlite3_compile_options",
      "//build/config/compiler:no_chromium_code",

      # Must be after no_chromium_code for warning flags to be ordered
      # correctly.
      ":sqlite_warnings",
    ]
  }
}

# Libfuzzer-based fuzzer test from SQLite source tree.
fuzzer_test("sqlite3_ossfuzz_fuzzer") {
  include_dirs = [ "." ]
  sources = [ "src/test/ossfuzz.c" ]
  deps = [ ":sqlite" ]
  dict = "fuzz/sql.dict"
}

source_set("sqlite3_lpm_fuzzer_core") {
  sources = [
    "fuzz/disabled_queries_parser.cc",
    "fuzz/disabled_queries_parser.h",
    "fuzz/sql_run_queries.cc",
    "fuzz/sql_run_queries.h",
  ]
  deps = [ ":sqlite" ]
  public_deps = [ ":sqlite3_lpm_fuzzer_input" ]
  configs += [
    ":sqlite_warnings",
    ":chromium_sqlite3_compile_options",
  ]
  all_dependent_configs = [
    ":lpm_fuzzer_omit_non_websql",
    ":chromium_sqlite3_compile_options",
  ]
}

source_set("sql_query_proto_to_string") {
  sources = [
    "fuzz/sql_query_proto_to_string.cc",
    "fuzz/sql_query_proto_to_string.h",
  ]
  deps = [ ":sqlite3_lpm_fuzzer_input" ]
}

# LPM-based fuzzer test.
fuzzer_test("sqlite3_lpm_fuzzer") {
  sources = [
    "fuzz/sql_fuzzer.cc",
    "fuzz/sql_query_proto_to_string.cc",
    "fuzz/sql_query_proto_to_string.h",
  ]
  deps = [
    ":sqlite3_lpm_fuzzer_core",
    "//third_party/libprotobuf-mutator",
  ]
  additional_configs = [ ":sqlite_warnings" ]
  libfuzzer_options = [
    "max_len=2111000",
    "len_control=0",
  ]
  seed_corpus = "fuzz/lpm_fuzzer_seed_corpus/"
}

# FTS3-focused LPM-based fuzzer test.
fuzzer_test("sqlite3_fts3_lpm_fuzzer") {
  sources = [
    "fuzz/sql_fuzzer.cc",
    "fuzz/sql_query_proto_to_string.cc",
    "fuzz/sql_query_proto_to_string.h",
  ]
  deps = [
    ":sqlite3_lpm_fuzzer_core",
    "//third_party/libprotobuf-mutator",
  ]
  additional_configs = [
    ":sqlite_warnings",
    ":sqlite3_fts3_lpm_fuzzer_config",
  ]
  libfuzzer_options = [
    "max_len=2111000",
    "len_control=0",
  ]
}

fuzzer_test("sqlite3_shadow_table_fuzzer") {
  sources = [ "fuzz/shadow_table_fuzzer.cc" ]
  deps = [ ":sqlite" ]
  additional_configs = [ ":sqlite_warnings" ]
}

fuzzer_test("sqlite3_select_printf_lpm_fuzzer") {
  sources = [
    "fuzz/sql_printf_fuzzer.cc",
    "fuzz/sql_query_proto_to_string.cc",
    "fuzz/sql_query_proto_to_string.h",
  ]
  deps = [
    ":sqlite3_lpm_fuzzer_core",
    "//third_party/libprotobuf-mutator",
  ]
  libfuzzer_options = [ "max_len=111000" ]
}

fuzzer_test("sqlite3_select_strftime_lpm_fuzzer") {
  sources = [
    "fuzz/sql_query_proto_to_string.cc",
    "fuzz/sql_query_proto_to_string.h",
    "fuzz/sql_strftime_fuzzer.cc",
  ]
  deps = [
    ":sqlite3_lpm_fuzzer_core",
    "//third_party/libprotobuf-mutator",
  ]
  libfuzzer_options = [ "max_len=111000" ]
}

fuzzer_test("sqlite3_select_expr_lpm_fuzzer") {
  sources = [
    "fuzz/sql_expr_fuzzer.cc",
    "fuzz/sql_query_proto_to_string.cc",
    "fuzz/sql_query_proto_to_string.h",
  ]
  deps = [
    ":sqlite3_lpm_fuzzer_core",
    "//third_party/libprotobuf-mutator",
  ]
  libfuzzer_options = [
    "max_len=111000",
    "len_control=0",
  ]
}

config("sqlite3_fts3_lpm_fuzzer_config") {
  defines = [ "FUZZ_FTS3" ]
}

config("lpm_fuzzer_omit_non_websql") {
  defines = [
    "FUZZ_OMIT_SAVEPOINT",
    "FUZZ_OMIT_PRAGMA",
  ]
}

proto_library("sqlite3_lpm_fuzzer_input") {
  sources = [
    "fuzz/icu_codes.proto",
    "fuzz/sql_queries.proto",
    "fuzz/sql_query_grammar.proto",
  ]
}

# Generates a good corpus for the sqlite_lpm_fuzzer
# Don't build this tool on Windows since it uses a POSIX-only API and because it
# only needs to be used on devs' machines.
if (use_fuzzing_engine && !is_win) {
  executable("sqlite3_lpm_corpus_gen") {
    sources = [
      "fuzz/sql_generate_corpus.cc",
      "fuzz/sql_query_proto_to_string.cc",
      "fuzz/sql_query_proto_to_string.h",
    ]
    deps = [
      ":sqlite3_lpm_fuzzer_core",
      "//base",
      "//third_party/protobuf:protobuf_full",
    ]
  }
}

config("sqlite3_dbfuzz2_config") {
  cflags = [ "-Wno-sign-compare" ]
  configs = [ ":sqlite_warnings" ]
}

# Upstream fuzzer that tests corrupted database files.
if (use_fuzzing_engine && !use_clang_coverage) {
  fuzzer_test("sqlite3_dbfuzz2_fuzzer") {
    include_dirs = [ "." ]
    sources = [ "src/test/dbfuzz2.c" ]
    deps = [ ":sqlite" ]
    additional_configs = [ ":sqlite3_dbfuzz2_config" ]
    seed_corpus = "fuzz/db_corpus"
  }
}