Move environment detection code to bytecode_detect.c.

This is in preparation for bug #2078, so that the bytecodes can turn themselves off
based on more than just the arch itself.
0.96
Török Edvin 16 years ago
parent dc0bf21281
commit c85060ff79
  1. 311
      libclamav/bytecode_detect.c
  2. 156
      libclamav/bytecode_detect.h
  3. 4
      libclamav/bytecode_nojit.c
  4. 83
      libclamav/c++/bytecode2llvm.cpp
  5. 142
      libclamav/c++/detect.cpp

@ -0,0 +1,311 @@
/*
* Detect environment for bytecode.
*
* Copyright (C) 2009-2010 Sourcefire, Inc.
*
* Authors: Török Edvin
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#include "target.h"
#include "cltypes.h"
#include "bytecode_detect.h"
#include "others.h"
#include <string.h>
#include <stdio.h>
#ifdef HAVE_UNAME_SYSCALL
#include <sys/utsname.h>
#endif
#define CHECK_ARCH(a) if (!strcmp(TARGET_ARCH_TYPE, #a)) env->arch = arch_##a
extern int have_clamjit;
static void cli_print_environment(struct cli_environment *env)
{
uint32_t id_a = env->platform_id_a;
uint32_t id_b = env->platform_id_b;
uint32_t id_c = env->platform_id_c;
/* the bytecode instruction that exactly identifies this platform */
/* the space separated groups can be a concrete value, or 0xff for ANY */
cli_dbgmsg("environment detected:\n");
cli_dbgmsg("check_platform(0x%08x, 0x%08x, 0x%08x)\n",
id_a, id_b, id_c);
cli_dbgmsg("check_platform(0x%02x %01x %01x %02x %02x,"
"0x%01x %01x %02x %02x %02x,"
"0x%02x %02x %02x %02x)\n",
env->os_category, env->arch, env->compiler,
env->functionality_level,
env->dconf_level,
env->big_endian,
env->sizeof_ptr,
(env->cpp_version >> 16)&0xff,
(env->cpp_version >> 8)&0xff,
env->cpp_version&0xff,
env->os_features,
(env->c_version >> 16)&0xff,
(env->c_version >> 8)&0xff,
env->c_version&0xff);
cli_dbgmsg("check_platform( OS CPU COM FL DCONF,BE PTR CXX VV.VV.VV, FLG CC VV.VV.VV)\n");
cli_dbgmsg("Engine version: %s\n", env->engine_version);
cli_dbgmsg("Host triple: %s\n", env->triple);
cli_dbgmsg("Host CPU: %s\n", env->cpu);
cli_dbgmsg("OS: %s\n", env->sysname);
cli_dbgmsg("OS release: %s\n", env->release);
cli_dbgmsg("OS version: %s\n", env->version);
cli_dbgmsg("OS hardware: %s\n", env->machine);
cli_dbgmsg("OS LLVM category: %d\n", env->os);
cli_dbgmsg("Has JIT compiled: %d\n", env->has_jit_compiled);
cli_dbgmsg("------------------------------------------------------\n");
}
#ifdef __linux__
static int detect_PaX(void)
{
char line[128];
int pax = 0;
FILE *f = fopen("/proc/self/status", "r");
if (!f)
return 0;
while (fgets(line, sizeof(line), f)) {
if (!memcmp(line, "PaX:", 4)) {
pax = 1;
if (!strchr(line,'m'))
pax = 2;
break;
}
}
fclose(f);
return 0;
}
static int detect_SELinux(void)
{
char line[128];
int selinux = 0;
int enforce = 0;
FILE *f = fopen("/proc/filesystems", "r");
if (!f)
return 0;
while (fgets(line, sizeof(line), f)) {
if (strstr(line, " selinuxfs\n")) {
selinux = 1;
break;
}
}
fclose(f);
if (!selinux)
return 0;
f = fopen("/selinux/enforce", "r");
if (f && fscanf(f, "%d", &enforce) == 1) {
if (enforce == 1)
selinux = 2;
if (enforce == -1)
selinux = 0;
}
fclose(f);
return selinux;
}
static void detect_os_features(uint8_t *os_features)
{
int features = 0;
switch (detect_PaX()) {
case 2:
features |= 1 << feature_pax_mprotect;
/* fall through */
case 1:
features |= 1 << feature_pax;
break;
default:
break;
}
switch (detect_SELinux()) {
case 2:
features |= 1 << feature_selinux_enforcing;
/* fall through */
case 1:
features |= 1 << feature_selinux;
break;
default:
break;
}
*os_features = features;
}
#else
static void detect_os_features(uint8_t *os_features)
{
*os_features = 0;
}
#endif
/* OS features :
* Linux: PaX << 2| SELinux << 1| mmap-RWX
* Other: mmap-RWX */
void cli_detect_environment(struct cli_environment *env)
{
memset(env, 0, sizeof(*env));
#if WORDS_BIGENDIAN == 0
env->big_endian = 0;
#else
env->big_endian = 1;
#endif
env->sizeof_ptr = sizeof(void*);
/* -- Detect arch -- */
CHECK_ARCH(i386);
else CHECK_ARCH(x86_64);
else if (!strcmp(TARGET_ARCH_TYPE,"ppc")) env->arch = arch_ppc32;/* llvm will fix ppc64 */
else CHECK_ARCH(arm);
else CHECK_ARCH(sparc);
else CHECK_ARCH(sparc64);
else CHECK_ARCH(mips);
else CHECK_ARCH(mips64);
else CHECK_ARCH(alpha);
else CHECK_ARCH(hppa1);
else CHECK_ARCH(hppa2);
else CHECK_ARCH(m68k);
else env->arch = arch_unknown;
/* -- Detect OS -- */
#ifdef C_AIX
env->os_category = os_aix;
#elif defined(C_BEOS)
env->os_category = os_beos;
/* DARWIN must come before BSD since it defines both */
#elif defined(C_DARWIN)
env->os_category = os_darwin;
#elif defined(C_BSD)
env->os_category = os_bsd;
#elif defined(C_GNU_HURD)
env->os_category = os_gnu_hurd;
#elif defined(C_HPUX)
env->os_category = os_hpux;
#elif defined(C_INTERIX)
env->os_category = os_interix;
#elif defined(C_IRIX)
env->os_category = os_irix;
#elif defined(C_KFREEBSD_GNU)
env->os_category = os_kfreebsd_gnu;
#elif defined(C_LINUX)
env->os_category = os_linux;
#elif defined(C_OS2)
env->os_category = os_os2;
#elif defined(C_OSF)
env->os_category = os_osf;
#elif defined(C_QNX6)
env->os_category = os_qnx6;
#elif defined(C_SOLARIS)
env->os_category = os_solaris;
#elif defined(_WIN64)
env->os_category = os_win64;
#elif defined(_WIN32)
env->os_category = os_win32;
#else
env->os_category = os_generic;
#endif
env->os = llvm_os_UnknownOS;
/* -- Detect compiler -- */
/* check GNUC last, because some other compilers might define it */
#ifdef __INTEL_COMPILER
env->compiler = compiler_intel;
env->c_version = __INTEL_COMPILER;
#elif defined(_MSC_VER)
env->compiler = compiler_msc;
env->c_version = _MSC_VER;
#elif defined(__SUNPRO_C)
env->compiler = compiler_sun;
env->c_version = __SUNPRO_C;
#elif defined(__GNUC__)
#ifdef __clang__
env->compiler = compiler_clang;
#elif defined(__llvm__)
env->compiler = compiler_llvm;
#else
env->compiler = compiler_gnuc;
#endif
env->c_version =
MAKE_VERSION(0, __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
#else
env->compiler = compiler_other;
env->c_version = 0;
#endif
env->cpp_version = 0;
env->has_jit_compiled = have_clamjit;
/* engine */
env->functionality_level = cl_retflevel();
env->dconf_level = CL_FLEVEL_DCONF;
INIT_STRFIELD(env->engine_version, cl_retver());
#ifdef HAVE_UNAME_SYSCALL
{
struct utsname name;
if (uname(&name) == 0) {
INIT_STRFIELD(env->sysname, name.sysname);
INIT_STRFIELD(env->release, name.release);
INIT_STRFIELD(env->version, name.version);
INIT_STRFIELD(env->machine, name.machine);
}
}
#endif
#ifdef _WIN32
{
OSVERSIONINFOEX info;
info.dwOSVersionInfoSize = sizeof(info);
if (GetVersionEx(&info) == 0 && info.dwPlatformId == VER_PLATFORM_WIN32_NT) {
if (info.wProductType == VER_NT_WORKSTATION)
INIT_STRFIELD(env->sysname, "Microsoft Windows");
else
INIT_STRFIELD(env->sysname, "Microsoft Windows Server");
snprintf(env->release, sizeof(env->release), "%d.%d SP%d.%d",
info.dwMajorVersion, info.dwMinorVersion,
info.wServicePackMajor, info.wServicePackMinor);
snprintf(env->version, sizeof(env->version),"Build %d",
info.dwBuildNumber);
}
}
#endif
if (!env->sysname[0]) {
INIT_STRFIELD(env->sysname, TARGET_OS_TYPE);
}
detect_os_features(&env->os_features);
cli_detect_env_jit(env);
env->platform_id_a = (env->os_category << 24) | (env->arch << 20) |
(env->compiler << 16) | (env->functionality_level << 8) |
(env->dconf_level);
env->platform_id_b = (env->big_endian << 28) | (env->sizeof_ptr << 24) |
env->cpp_version;
env->platform_id_c = (env->os_features << 24) | env->c_version;
cli_print_environment(env);
}

@ -0,0 +1,156 @@
/*
* Copyright (C) 2009 Sourcefire, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef BYTECODE_DETECT_H
#define BYTECODE_DETECT_H
/* mostly from m4/acinclude.m4 */
enum arch_list {
arch_unknown=0,
arch_i386,
arch_x86_64,
arch_ppc32,
arch_ppc64,
arch_arm,
arch_sparc,
arch_sparc64,
arch_mips,
arch_mips64,
arch_alpha,
arch_hppa1,
arch_hppa2,
arch_m68k,
arch_ANY = 0xf
};
/* from ClamAV's configure.in */
enum os_kind_conf {
os_unknown=0,
os_aix,
os_beos,
os_bsd,
os_darwin,
os_gnu_hurd,
os_hpux,
os_interix,
os_irix,
os_kfreebsd_gnu,
os_linux,
os_os2,
os_osf,
os_qnx6,
os_solaris,
os_win32,
os_win64,
os_ANY = 0xff
};
enum os_kind_llvm {
llvm_os_UnknownOS=0,
llvm_os_AuroraUX,
llvm_os_Cygwin,
llvm_os_Darwin,
llvm_os_DragonFly,
llvm_os_FreeBSD,
llvm_os_Linux,
llvm_os_Lv2,
llvm_os_MinGW32,
llvm_os_MinGW64,
llvm_os_NetBSD,
llvm_os_OpenBSD,
llvm_os_Psp,
llvm_os_Solaris,
llvm_os_Win32,
llvm_os_Haiku,
llvm_os_ANY = 0xff
};
/* the ones from clamconf */
enum compiler_list {
compiler_unknown = 0,
compiler_gnuc,
compiler_llvm,
compiler_clang,
compiler_intel,
compiler_msc,
compiler_sun,
compiler_other,
compiler_ANY = 0xf
};
enum endian_list {
endian_little=0,
endian_big=1,
endian_ANY=0xf
};
enum os_feature_bits {
feature_map_rwx = 0,
feature_selinux = 1,
feature_selinux_enforcing = 2,
feature_pax = 3,
feature_pax_mprotect = 4
};
struct cli_environment {
uint32_t platform_id_a;
uint32_t platform_id_b;
uint32_t platform_id_c;
uint32_t c_version;
uint32_t cpp_version; /* LLVM only */
/* engine */
uint32_t functionality_level;
uint32_t dconf_level;
int8_t engine_version[65];
/* detailed runtime info */
int8_t triple[65];/* LLVM only */
int8_t cpu[65];/* LLVM only */
/* uname */
int8_t sysname[65];
int8_t release[65];
int8_t version[65];
int8_t machine[65];
/* build time */
uint8_t big_endian;
uint8_t sizeof_ptr;
uint8_t arch;
uint8_t os_category;/* from configure */
uint8_t os;/* from LLVM if available */
uint8_t compiler;
uint8_t has_jit_compiled;
uint8_t os_features;
uint8_t reserved0;
};
#ifndef __CLAMBC__
#define MAKE_VERSION(a,b,c,d) ((a << 24) | (b << 16) | (c << 8) | d)
#define INIT_STRFIELD(field, value) do {\
strncpy((char*)(field), (value), sizeof(field)-1);\
(field)[sizeof(field)-1]=0;\
} while (0)
#endif
void cli_detect_env_jit(struct cli_environment *env);
void cli_detect_env(struct cli_environment *env);
#endif

@ -78,3 +78,7 @@ void cli_printcxxver()
{
/* Empty */
}
void cli_detect_env_jit(struct cli_environment *env)
{
/* Empty */
}

@ -1800,7 +1800,7 @@ int cli_bytecode_prepare_jit(struct cli_all_bc *bcs)
for (unsigned i=0;i<bcs->count;i++) {
const struct cli_bc *bc = &bcs->all_bcs[i];
if (bc->state == bc_skip)
if (bc->state == bc_skip || bc->state == bc_interp)
continue;
LLVMCodegen Codegen(bc, M, &CF, bcs->engine->compiledFunctions, EE,
OurFPM, apiFuncs, apiMap);
@ -1872,87 +1872,6 @@ int bytecode_init(void)
int cli_bytecode_init_jit(struct cli_all_bc *bcs, unsigned dconfmask)
{
LLVMApiScopedLock scopedLock;
bcs->engine = NULL;
Triple triple(sys::getHostTriple());
if (cli_debug_flag)
errs() << "host triple is: " << sys::getHostTriple() << "\n";
enum Triple::ArchType arch = triple.getArch();
switch (arch) {
case Triple::arm:
if (!(dconfmask & BYTECODE_JIT_ARM)) {
if (cli_debug_flag)
errs() << "host triple is: " << sys::getHostTriple() << "\n";
return 0;
}
break;
case Triple::ppc:
case Triple::ppc64:
if (!(dconfmask & BYTECODE_JIT_PPC)) {
if (cli_debug_flag)
errs() << "JIT disabled for ppc\n";
return 0;
}
break;
case Triple::x86:
case Triple::x86_64:
if (!(dconfmask & BYTECODE_JIT_X86)) {
if (cli_debug_flag)
errs() << "JIT disabled for x86\n";
return 0;
}
break;
default:
errs() << "Not supported architecture for " << triple.str() << "\n";
return CL_EBYTECODE;
}
std::string cpu = sys::getHostCPUName();
if (cli_debug_flag)
errs() << "host cpu is: " << cpu << "\n";
if (!cpu.compare("i386") ||
!cpu.compare("i486")) {
bcs->engine = 0;
DEBUG(errs() << "i[34]86 detected, falling back to interpreter (JIT needs pentium or better\n");
/* i386 and i486 has to fallback to interpreter */
have_clamjit=0;
return 0;
}
std::string ErrMsg;
sys::MemoryBlock B = sys::Memory::AllocateRWX(4096, NULL, &ErrMsg);
if (B.base() == 0) {
errs() << MODULE << ErrMsg << "\n";
#ifdef __linux__
errs() << MODULE << "SELinux or PaX is preventing 'execmem' access."
<< "Run 'setsebool -P clamd_use_jit on' or 'paxctl -cm <executable>' to allow access\n";
#endif
errs() << MODULE << "falling back to interpreter mode\n";
have_clamjit=0;
return 0;
} else {
sys::Memory::ReleaseRWX(B);
#ifdef __linux__
FILE *f = fopen("/proc/self/status", "r");
if (f) {
char line[128];
while (fgets(line, sizeof(line), f)) {
if (!memcmp(line, "PaX:", 4)) {
if (cli_debug_flag) {
errs() << "bytecode JIT: PaX found: " << line;
}
if (!strchr(line,'m')) {
errs() << MODULE << "PaX is preventing MPROTECT, use 'paxctl -cm <executable>' to allow\n";
errs() << MODULE << "falling back to interpreter mode\n";
fclose(f);
have_clamjit=0;
return 0;
}
}
}
fclose(f);
}
#endif
}
bcs->engine = new(std::nothrow) cli_bcengine;
if (!bcs->engine)
return CL_EMEM;

@ -0,0 +1,142 @@
/*
* JIT detection for ClamAV bytecode.
*
* Copyright (C) 2010 Sourcefire, Inc.
*
* Authors: Török Edvin
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include "llvm/ADT/Triple.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/System/Host.h"
#include "llvm/System/DataTypes.h"
#include "llvm/System/Memory.h"
#include "llvm/Config/config.h"
extern "C" {
#include "bytecode_detect.h"
}
using namespace llvm;
static void warn_assumptions(const char *msg, int a, int b)
{
errs() << "LibClamAV Warning: libclamav and llvm make inconsistent"
<< "assumptions about " << msg << ": " <<
a << " and " << b << "."
<< "Please report to http://bugs.clamav.net\n";
}
#define CASE_OS(theos, compat) case Triple::theos:\
env->os = llvm_os_##theos;\
if (env->os_category != compat)\
warn_assumptions("Operating System", env->os_category, Triple::theos);\
break
void cli_detect_env_jit(struct cli_environment *env)
{
std::string host_triple = sys::getHostTriple();
INIT_STRFIELD(env->triple, host_triple.c_str());
std::string cpu = sys::getHostCPUName();
INIT_STRFIELD(env->cpu, cpu.c_str());
if (env->big_endian != sys::isBigEndianHost()) {
warn_assumptions("host endianness", env->big_endian, sys::isBigEndianHost());
env->big_endian = sys::isBigEndianHost();
}
#ifdef __GNUC__
env->cpp_version = MAKE_VERSION(0, __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
#elif defined (__INTEL_COMPILER)
env->cpp_version = __INTEL_COMPILER;
#elif defined (_MSC_VER)
env->cpp_version = _MSC_VER;
#endif
Triple triple(host_triple);
// CPU architecture
enum Triple::ArchType arch = triple.getArch();
enum arch_list earch;
bool conflicts = false;
switch (arch) {
case Triple::arm:
earch = arch_arm;
if (env->arch != earch) conflicts = true;
break;
case Triple::ppc:
earch = arch_ppc32;
if (env->arch != earch) conflicts = true;
break;
case Triple::ppc64:
earch = arch_ppc64;
// ppc64 is fixed up by llvm
if (env->arch != arch_ppc32 &&
env->arch != arch_ppc64) conflicts = true;
break;
case Triple::x86:
earch = arch_i386;
if (env->arch != earch) conflicts = true;
break;
case Triple::x86_64:
earch = arch_x86_64;
if (env->arch != earch) conflicts = true;
break;
default:
earch = arch_unknown;
break;
}
if (conflicts)
warn_assumptions("CPU architecture", env->arch, earch);
if (earch != arch_unknown)
env->arch = earch;
// OS
Triple::OSType os = triple.getOS();
switch (os) {
case Triple::UnknownOS:
env->os = llvm_os_UnknownOS;
break;
CASE_OS(AuroraUX, os_solaris);
CASE_OS(Cygwin, os_win32);
CASE_OS(Darwin, os_darwin);
CASE_OS(DragonFly, os_bsd);
CASE_OS(FreeBSD, os_bsd);
CASE_OS(Linux, os_linux);
CASE_OS(Lv2, os_unknown);
CASE_OS(MinGW32, os_win32);
CASE_OS(MinGW64, os_win64);
CASE_OS(NetBSD, os_bsd);
CASE_OS(OpenBSD, os_bsd);
CASE_OS(Psp, os_unknown);
CASE_OS(Solaris, os_solaris);
CASE_OS(Win32, os_win32);
CASE_OS(Haiku, os_unknown);
}
// mmap RWX
std::string ErrMsg;
sys::MemoryBlock B = sys::Memory::AllocateRWX(4096, NULL, &ErrMsg);
if (B.base() == 0) {
errs() << "LibClamAV Warning: RWX mapping denied: " << ErrMsg << "\n";
} else {
env->os_features |= 1 << feature_map_rwx;
sys::Memory::ReleaseRWX(B);
}
}
Loading…
Cancel
Save