Generate Rust-bindings for some internal C-headers

Use bindgen to generate Rust-bindings for some libclamav internal
functions and structures in an new "sys.rs" module.

"sys.rs" will be generated at build-time and unfortunately must be
dropped in the source/libclamav_rust/src directory rather than under the
build directory. As far as I know, Cargo/Rust provide no way to set an
include path to the build directory to separately place generated files.
TODO: Verify if this is true and move sys.rs to the build directory if
possible.

Using the new bindings with:
- the logging module.
- the cdiff module.

Also:
- Removed clamav_rust.h from .gitignore, because we generate it in the
  build directory now.
- Removed the hand-written bindings from the cbindgen exclusions.
  lib.rs has an annotation that prevents cbindgen from looking at sys.rs.
- Fixed a `u8` -> `c_char` type issue in cdiff in the cli_getdsig() call
  parameters.
This commit is contained in:
micasnyd
2021-12-20 09:39:40 -08:00
committed by Micah Snyder
parent 7089a788b3
commit ba0a21c31f
9 changed files with 122 additions and 57 deletions

7
.gitignore vendored
View File

@@ -228,12 +228,13 @@ libclamav/c++/llvm/tools/llvmc/plugins/Base/Base.td
debug/
target/
# Generated by libclamav_rust/build.rs in IDEs
libclamav_rust/clamav_rust.h
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
# The name of our generated ClamAV-Rust bindings file.
libclamav_rust/src/sys.rs

View File

@@ -856,6 +856,12 @@ inline void cli_dbgmsg(const char *str, ...) __attribute__((format(printf, 1, 2)
inline void cli_dbgmsg(const char *str, ...);
#endif
#ifdef __GNUC__
void cli_dbgmsg_no_inline(const char *str, ...) __attribute__((format(printf, 1, 2)));
#else
void cli_dbgmsg_no_inline(const char *str, ...);
#endif
#ifdef HAVE_CLI_GETPAGESIZE
#undef HAVE_CLI_GETPAGESIZE
#endif

View File

@@ -180,6 +180,14 @@ inline void cli_dbgmsg(const char *str, ...)
}
}
void cli_dbgmsg_no_inline(const char *str, ...)
{
if (UNLIKELY(cli_get_debug_flag())) {
MSGCODE(buff, len, "LibClamAV debug: ");
fputs(buff, stderr);
}
}
int cli_matchregex(const char *str, const char *regex)
{
regex_t reg;

View File

@@ -19,3 +19,4 @@ name = "clamav_rust"
[build-dependencies]
cbindgen = "0.20"
bindgen = "0.59"

View File

@@ -1,6 +1,8 @@
use std::env;
use std::path::{Path, PathBuf};
use bindgen::builder;
// Note to maintainers: this is currently a hybrid of examination of the
// CMake environment, and leaning on Rust `cfg` elements. Ideally, it
// should be possible to work in this space (e.g., execute tests from an
@@ -14,7 +16,7 @@ use std::path::{Path, PathBuf};
// A list of environment variables to query to determine additional libraries
// that need to be linked to resolve dependencies.
const LIB_ENV_LINK: [&str; 12] = [
const LIB_ENV_LINK: &[&str] = &[
"LIBSSL",
"LIBCRYPTO",
"LIBZ",
@@ -30,13 +32,47 @@ const LIB_ENV_LINK: [&str; 12] = [
];
// The same, but additional values to check on Windows platforms
const LIB_ENV_LINK_WINDOWS: [&str; 2] = ["LIBPTHREADW32", "LIBWIN32COMPAT"];
const LIB_ENV_LINK_WINDOWS: &[&str] = &["LIBPTHREADW32", "LIBWIN32COMPAT"];
// Additional [verbatim] libraries to link on Windows platforms
const LIB_LINK_WINDOWS: [&str; 4] = ["wsock32", "ws2_32", "Shell32", "User32"];
const LIB_LINK_WINDOWS: &[&str] = &["wsock32", "ws2_32", "Shell32", "User32"];
// Windows library names that must have the leading `lib` trimmed (if encountered)
const WINDOWS_TRIM_LOCAL_LIB: [&str; 2] = ["libclamav", "libclammspack"];
const WINDOWS_TRIM_LOCAL_LIB: &[&str] = &["libclamav", "libclammspack"];
// Generate bindings for these functions:
const BINDGEN_FUNCTIONS: &[&str] = &[
"cli_ctx",
"cli_warnmsg",
"cli_dbgmsg_no_inline",
"cli_infomsg_simple",
"cli_errmsg",
"cli_append_virus",
"cli_versig2",
"cli_getdsig",
"cli_get_debug_flag",
];
// Generate bindings for these types (structs, enums):
const BINDGEN_TYPES: &[&str] = &["cli_matcher", "cli_ac_data", "cli_ac_result"];
// Find the required functions and types in these headers:
const BINDGEN_HEADERS: &[&str] = &[
"../libclamav/matcher.h",
"../libclamav/matcher-ac.h",
"../libclamav/others.h",
"../libclamav/dsig.h",
];
// Find the required headers in these directories:
const BINDGEN_INCLUDE_PATHS: &[&str] = &[
"-I../libclamav",
"-I../libclamunrar_iface",
"-I../libclammspack",
];
// Write the bindings to this file:
const BINDGEN_OUTPUT_FILE: &str = "src/sys.rs";
const C_HEADER_OUTPUT: &str = "clamav_rust.h";
@@ -58,14 +94,54 @@ fn main() -> Result<(), &'static str> {
// We only want to execute cbindgen for `cargo build`, not `cargo test`.
// FindRust.cmake defines $CARGO_CMD so we can differentiate.
if "build" == env::var("CARGO_CMD").or(Ok("".to_string()))? {
let cargo_cmd = env::var("CARGO_CMD").unwrap_or("".into());
if cargo_cmd == "build" {
execute_bindgen()?;
execute_cbindgen()?;
} else {
eprintln!("NOTE: Not performing cbindgen as CARGO_CMD != build");
eprintln!("NOTE: Not generating bindings because CARGO_CMD != build");
}
Ok(())
}
/// Use bindgen to generate Rust bindings to call into C libraries.
fn execute_bindgen() -> Result<(), &'static str> {
let build_dir = PathBuf::from(env::var("CARGO_TARGET_DIR").unwrap_or(".".into()));
let build_include_path = format!("-I{}", build_dir.join("..").to_str().unwrap());
// Configure and generate bindings.
let mut builder = builder()
// Silence code-style warnings for generated bindings.
.raw_line("#![allow(non_snake_case, non_camel_case_types, non_upper_case_globals)]")
// Make the bindings pretty.
.rustfmt_bindings(true)
// Enable bindgen to find generated headers in the build directory, too.
.clang_arg(build_include_path);
for &include_path in BINDGEN_INCLUDE_PATHS {
builder = builder.clang_arg(include_path);
}
for &header in BINDGEN_HEADERS {
builder = builder.header(header);
}
for &c_function in BINDGEN_FUNCTIONS {
builder = builder.allowlist_function(c_function);
}
for &c_type in BINDGEN_TYPES {
builder = builder.allowlist_type(c_type);
}
// Generate!
let bindings = builder.generate().unwrap();
// Write the generated bindings to an output file.
bindings.write_to_file(BINDGEN_OUTPUT_FILE).unwrap();
Ok(())
}
/// Use cbindgen to generate C-header's for Rust static libraries.
fn execute_cbindgen() -> Result<(), &'static str> {
let crate_dir = env::var("CARGO_MANIFEST_DIR").or(Err("CARGO_MANIFEST_DIR not specified"))?;
let build_dir = PathBuf::from(env::var("CARGO_TARGET_DIR").unwrap_or(".".into()));
@@ -86,15 +162,15 @@ fn detect_clamav_build() -> Result<(), &'static str> {
if search_and_link_lib("LIBCLAMAV")? {
eprintln!("NOTE: LIBCLAMAV defined. Examining LIB* environment variables");
// Need to link with libclamav dependencies
for var in &LIB_ENV_LINK {
for var in LIB_ENV_LINK {
let _ = search_and_link_lib(var);
}
if cfg!(windows) {
for var in &LIB_ENV_LINK_WINDOWS {
for var in LIB_ENV_LINK_WINDOWS {
let _ = search_and_link_lib(var);
}
for lib in &LIB_LINK_WINDOWS {
for lib in LIB_LINK_WINDOWS {
println!("cargo:rustc-link-lib={}", lib);
}
} else {

View File

@@ -17,15 +17,8 @@ after_includes = ""
[export]
include = ["cdiff_apply", "script2cdiff"]
exclude = [
"cli_dbgmsg",
"cli_errmsg",
"cli_infomsg_simple",
"cli_warnmsg",
"cli_versig2",
"cli_get_debug_flag",
"cli_getdsig",
]
exclude = []
# prefix = "CAPI_"
item_types = []
renaming_overrides_prefixing = false

View File

@@ -24,7 +24,7 @@ use std::{
fs::{self, File, OpenOptions},
io::{prelude::*, BufReader, BufWriter, Read, Seek, SeekFrom, Write},
iter::*,
os::raw::{c_char, c_uchar},
os::raw::c_char,
process,
};
@@ -34,6 +34,8 @@ use log::{debug, error, warn};
use sha2::{Digest, Sha256};
use thiserror::Error;
use crate::sys;
/// Maximum size of a digital signature
const MAX_SIG_SIZE: usize = 350;
@@ -255,28 +257,9 @@ impl<'a> XchgOp<'a> {
}
}
extern "C" {
fn cli_versig2(
digest: *const c_uchar,
dsig: *const c_char,
n: *const c_char,
e: *const c_char,
) -> i32;
fn cli_getdsig(
host: *const u8,
user: *const u8,
data: *const u8,
datalen: u32,
mode: u8,
) -> *const c_char;
fn cli_get_debug_flag() -> u8;
}
fn is_debug_enabled() -> bool {
unsafe {
let debug_flag = cli_get_debug_flag();
let debug_flag = sys::cli_get_debug_flag();
// Return true if debug_flag is not 0
!matches!(debug_flag, 0)
}
@@ -418,9 +401,9 @@ pub fn script2cdiff(script_file_name: &str, builder: &str, server: &str) -> Resu
// These strings should not contain interior NULs
let server = CString::new(server).unwrap();
let builder = CString::new(builder).unwrap();
let dsig_ptr = cli_getdsig(
server.as_c_str().as_ptr() as *const u8,
builder.as_c_str().as_ptr() as *const u8,
let dsig_ptr = sys::cli_getdsig(
server.as_c_str().as_ptr() as *const c_char,
builder.as_c_str().as_ptr() as *const c_char,
sha256.to_vec().as_ptr(),
32,
2,
@@ -511,7 +494,7 @@ pub fn cdiff_apply(file: &mut File, mode: ApplyMode) -> Result<(), CdiffError> {
let n = CString::new(PUBLIC_KEY_MODULUS).unwrap();
let e = CString::new(PUBLIC_KEY_EXPONENT).unwrap();
let versig_result = unsafe {
cli_versig2(
sys::cli_versig2(
sha256.to_vec().as_ptr(),
dsig_cstring.as_ptr(),
n.as_ptr() as *const c_char,

View File

@@ -20,6 +20,9 @@
* MA 02110-1301, USA.
*/
/// cbindgen:ignore
pub mod sys;
pub mod cdiff;
pub mod logging;
pub mod util;

View File

@@ -21,16 +21,10 @@
*/
use std::ffi::CString;
use std::os::raw::c_char;
use log::{set_max_level, Level, LevelFilter, Metadata, Record};
extern "C" {
fn cli_warnmsg(str: *const c_char, ...) -> ();
fn cli_dbgmsg(str: *const c_char, ...) -> ();
fn cli_infomsg_simple(str: *const c_char, ...) -> ();
fn cli_errmsg(str: *const c_char, ...) -> ();
}
use crate::sys;
pub struct ClamLogger;
@@ -46,16 +40,16 @@ impl log::Log for ClamLogger {
match record.level() {
Level::Debug => unsafe {
cli_dbgmsg(ptr);
sys::cli_dbgmsg_no_inline(ptr);
},
Level::Error => unsafe {
cli_errmsg(ptr);
sys::cli_errmsg(ptr);
},
Level::Info => unsafe {
cli_infomsg_simple(ptr);
sys::cli_infomsg_simple(ptr);
},
Level::Warn => unsafe {
cli_warnmsg(ptr);
sys::cli_warnmsg(ptr);
},
_ => {}
}