Merge pull request #425 from theopolis/feature-catching-exceptions

Fix unwind exception catching
This commit is contained in:
Teddy Reed 2014-11-14 01:43:55 -08:00
commit c0dc2720fb
11 changed files with 218 additions and 67 deletions

View File

@ -11,17 +11,19 @@
#include "osquery/database/results.h"
#ifndef STR
#define STR_OF(x) #x
#define STR(x) STR_OF(x)
#endif
namespace osquery {
/**
* @brief The version of osquery
*/
extern const std::string kVersion;
/// Use a macro for the version literal, set the kVersion symbol in the library.
#ifndef STR
#define STR_OF(x) #x
#define STR(x) STR_OF(x)
#endif
#define OSQUERY_VERSION STR(OSQUERY_BUILD_VERSION)
/**

View File

@ -223,13 +223,6 @@ class DBHandle {
*/
rocksdb::ColumnFamilyHandle* getHandleForColumnFamily(const std::string& cf);
/**
* @brief Determine if a DBInstance can be created for the requested env.
*
* @return an estimate of a sane environment as an exception.
*/
static void requireInstance(const std::string& path, bool in_memory);
private:
/////////////////////////////////////////////////////////////////////////////
// Private members

View File

@ -18,6 +18,7 @@ SET(OSQUERY_APPLE_LIBS
glog
)
# Warning: Do not statically compile unwind
SET(OSQUERY_LINUX_LIBS
libgflags.a
libglog.a
@ -28,6 +29,7 @@ SET(OSQUERY_LINUX_LIBS
libboost_filesystem.a
libboost_program_options.a
libboost_regex.a
unwind
udev
blkid
rt
@ -37,7 +39,6 @@ SET(OSQUERY_UBUNTU_LIBS
libz.a
libbz2.a
libsnappy.a
libunwind.a
liblzma.a
)
@ -45,7 +46,6 @@ SET(OSQUERY_CENTOS_LIBS
libz.so
libbz2.so
libsnappy.so
libunwind.so
liblzma.so
libproc.so
)

View File

@ -44,6 +44,16 @@ DBHandle::DBHandle(const std::string& path, bool in_memory) {
options_.create_if_missing = true;
options_.create_missing_column_families = true;
if (in_memory) {
// Remove when upgrading to RocksDB 3.3
// options_.env = rocksdb::NewMemEnv(rocksdb::Env::Default());
throw std::domain_error("Required RocksDB 3.3 (and setMemEnv)");
}
if (pathExists(path).ok() && !isWritable(path).ok()) {
throw std::domain_error("Cannot write to RocksDB path: " + path);
}
column_families_.push_back(rocksdb::ColumnFamilyDescriptor(
rocksdb::kDefaultColumnFamilyName, rocksdb::ColumnFamilyOptions()));
@ -56,18 +66,6 @@ DBHandle::DBHandle(const std::string& path, bool in_memory) {
rocksdb::DB::Open(options_, path, column_families_, &handles_, &db_);
}
void DBHandle::requireInstance(const std::string& path, bool in_memory) {
if (in_memory) {
// Remove when upgrading to RocksDB 3.3
// options_.env = rocksdb::NewMemEnv(rocksdb::Env::Default());
throw std::domain_error("Required RocksDB 3.3 (and setMemEnv)");
}
if (pathExists(path).ok() && !isWritable(path).ok()) {
throw std::domain_error("Cannot write to RocksDB path: " + path);
}
}
DBHandle::~DBHandle() {
for (auto handle : handles_) {
delete handle;
@ -92,14 +90,6 @@ std::shared_ptr<DBHandle> DBHandle::getInstanceAtPath(const std::string& path) {
std::shared_ptr<DBHandle> DBHandle::getInstance(const std::string& path,
bool in_memory) {
static bool valid_instance = false;
if (!valid_instance) {
// Throw any possible exceptions before the accessor.
// Workaround for issue #423
requireInstance(path, in_memory);
valid_instance = true;
}
static std::shared_ptr<DBHandle> db_handle =
std::shared_ptr<DBHandle>(new DBHandle(path, in_memory));
return db_handle;

View File

@ -2,8 +2,8 @@
#pragma once
#include <stdio.h>
#include <sqlite3.h>
#include <stdio.h>
namespace osquery {
namespace tables {

View File

@ -0,0 +1,137 @@
#ifndef _UAPI_INET_DIAG_H_
#define _UAPI_INET_DIAG_H_
#include <linux/types.h>
/* Just some random number */
#define TCPDIAG_GETSOCK 18
#define DCCPDIAG_GETSOCK 19
#define INET_DIAG_GETSOCK_MAX 24
/* Socket identity */
struct inet_diag_sockid {
__be16 idiag_sport;
__be16 idiag_dport;
__be32 idiag_src[4];
__be32 idiag_dst[4];
__u32 idiag_if;
__u32 idiag_cookie[2];
#define INET_DIAG_NOCOOKIE (~0U)
};
/* Request structure */
struct inet_diag_req {
__u8 idiag_family; /* Family of addresses. */
__u8 idiag_src_len;
__u8 idiag_dst_len;
__u8 idiag_ext; /* Query extended information */
struct inet_diag_sockid id;
__u32 idiag_states; /* States to dump */
__u32 idiag_dbs; /* Tables to dump (NI) */
};
struct inet_diag_req_v2 {
__u8 sdiag_family;
__u8 sdiag_protocol;
__u8 idiag_ext;
__u8 pad;
__u32 idiag_states;
struct inet_diag_sockid id;
};
enum {
INET_DIAG_REQ_NONE,
INET_DIAG_REQ_BYTECODE,
};
#define INET_DIAG_REQ_MAX INET_DIAG_REQ_BYTECODE
/* Bytecode is sequence of 4 byte commands followed by variable arguments.
* All the commands identified by "code" are conditional jumps forward:
* to offset cc+"yes" or to offset cc+"no". "yes" is supposed to be
* length of the command and its arguments.
*/
struct inet_diag_bc_op {
unsigned char code;
unsigned char yes;
unsigned short no;
};
enum {
INET_DIAG_BC_NOP,
INET_DIAG_BC_JMP,
INET_DIAG_BC_S_GE,
INET_DIAG_BC_S_LE,
INET_DIAG_BC_D_GE,
INET_DIAG_BC_D_LE,
INET_DIAG_BC_AUTO,
INET_DIAG_BC_S_COND,
INET_DIAG_BC_D_COND,
};
struct inet_diag_hostcond {
__u8 family;
__u8 prefix_len;
int port;
__be32 addr[0];
};
/* Base info structure. It contains socket identity (addrs/ports/cookie)
* and, alas, the information shown by netstat. */
struct inet_diag_msg {
__u8 idiag_family;
__u8 idiag_state;
__u8 idiag_timer;
__u8 idiag_retrans;
struct inet_diag_sockid id;
__u32 idiag_expires;
__u32 idiag_rqueue;
__u32 idiag_wqueue;
__u32 idiag_uid;
__u32 idiag_inode;
};
/* Extensions */
enum {
INET_DIAG_NONE,
INET_DIAG_MEMINFO,
INET_DIAG_INFO,
INET_DIAG_VEGASINFO,
INET_DIAG_CONG,
INET_DIAG_TOS,
INET_DIAG_TCLASS,
INET_DIAG_SKMEMINFO,
INET_DIAG_SHUTDOWN,
};
#define INET_DIAG_MAX INET_DIAG_SHUTDOWN
/* INET_DIAG_MEM */
struct inet_diag_meminfo {
__u32 idiag_rmem;
__u32 idiag_wmem;
__u32 idiag_fmem;
__u32 idiag_tmem;
};
/* INET_DIAG_VEGASINFO */
struct tcpvegas_info {
__u32 tcpv_enabled;
__u32 tcpv_rttcnt;
__u32 tcpv_rtt;
__u32 tcpv_minrtt;
};
#endif /* _UAPI_INET_DIAG_H_ */

View File

@ -4,10 +4,8 @@
#include <arpa/inet.h>
#include <asm/types.h>
#include <linux/inet_diag.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/sock_diag.h>
#include <linux/tcp.h>
#include <netinet/in.h>
#include <pwd.h>
@ -27,6 +25,14 @@
#include "osquery/database.h"
#include "osquery/logger.h"
// From uapi/linux/sock_diag.h
// From linux/sock_diag.h (<= 3.6)
#ifndef SOCK_DIAG_BY_FAMILY
#define SOCK_DIAG_BY_FAMILY 20
#endif
#include "inet_diag.h"
namespace osquery {
namespace tables {

View File

@ -28,7 +28,7 @@ class {{class_name}} {
struct sqlite3_{{table_name}} {
int n;
{% for col in schema %}\
std::vector<{{col.type.type}}> {{col.name}};
std::vector<{{col.type.type}}> xCol_{{col.name}};
{% endfor %}\
};
@ -75,7 +75,7 @@ int {{table_name_cc}}Column(
{% if col.type.affinity == "TEXT" %}\
sqlite3_result_text(
ctx,
(pVtab->pContent->{{col.name}}[pCur->row]).c_str(),
(pVtab->pContent->xCol_{{col.name}}[pCur->row]).c_str(),
-1,
nullptr
);
@ -83,13 +83,13 @@ int {{table_name_cc}}Column(
{% if col.type.affinity == "INTEGER" %}\
sqlite3_result_int(
ctx,
({{col.type.type}})pVtab->pContent->{{col.name}}[pCur->row]
({{col.type.type}})pVtab->pContent->xCol_{{col.name}}[pCur->row]
);
{% endif %}\
{% if col.type.affinity == "BIGINT" %}\
sqlite3_result_int64(
ctx,
({{col.type.type}})pVtab->pContent->{{col.name}}[pCur->row]
({{col.type.type}})pVtab->pContent->xCol_{{col.name}}[pCur->row]
);
{% endif %}\
break;
@ -112,7 +112,7 @@ int {{table_name_cc}}Filter(
pCur->row = 0;
{% for col in schema %}\
pVtab->pContent->{{col.name}}.clear();
pVtab->pContent->xCol_{{col.name}}.clear();
{% endfor %}\
{% if class_name != "" %}
@ -122,30 +122,32 @@ int {{table_name_cc}}Filter(
{% endif %}
{% for col in schema %}\
{% if col.type.affinity == "TEXT" %}\
pVtab->pContent->{{col.name}}.push_back(row["{{col.name}}"]);
pVtab->pContent->xCol_{{col.name}}.push_back(row["{{col.name}}"]);
{% endif %}\
{% if col.type.affinity == "INTEGER" %}\
try {
pVtab->pContent->{{col.name}}\
pVtab->pContent->xCol_{{col.name}}\
.push_back(boost::lexical_cast<{{col.type.type}}>(row["{{col.name}}"]));
} catch (const boost::bad_lexical_cast& e) {
LOG(WARNING) << "Error casting " << row["{{col.name}}"] << " to {{col.type.type}}";
pVtab->pContent->{{col.name}}.push_back(-1);
LOG(WARNING) << "Error casting {{col.name}} ("
<< row["{{col.name}}"] << ") to {{col.type.type}}";
pVtab->pContent->xCol_{{col.name}}.push_back(-1);
}
{% endif %}\
{% if col.type == "BIGINT" %}\
try {
pVtab->pContent->{{col.name}}\
pVtab->pContent->xCol_{{col.name}}\
.push_back(boost::lexical_cast<{{col.type.type}}>(row["{{col.name}}"]));
} catch (const boost::bad_lexical_cast& e) {
LOG(WARNING) << "Error casting " << row["{{col.name}}"] << " to {{col.type.type}}";
pVtab->pContent->{{col.name}}.push_back(-1);
LOG(WARNING) << "Error casting {{col.name}} ("
<< row["{{col.name}}"] << ") to {{col.type.type}}";
pVtab->pContent->xCol_{{col.name}}.push_back(-1);
}
{% endif %}\
{% endfor %}\
}
pVtab->pContent->n = pVtab->pContent->{{schema[0].name}}.size();
pVtab->pContent->n = pVtab->pContent->xCol_{{schema[0].name}}.size();
return SQLITE_OK;
}

View File

@ -16,7 +16,8 @@ import uuid
from gentable import Column, ForeignKey, \
table_name, schema, implementation, description, table, \
DataType, BIGINT, DATE, DATETIME, INTEGER, TEXT
DataType, BIGINT, DATE, DATETIME, INTEGER, TEXT \
is_blacklisted
# the log format for the logging module
LOG_FORMAT = "%(levelname)s [Line %(lineno)d]: %(message)s"
@ -122,6 +123,13 @@ def main(argc, argv):
logging.error("Cannot parse profile data: %s" % (str(e)))
exit(2)
# Read in the optional list of blacklisted tables
blacklist = None
blacklist_path = os.path.join(args.tables, "blacklist")
if os.path.exists(blacklist_path):
with open(blacklist_path, "r") as fh:
blacklist = fh.read()
categories = {}
for base, folders, files in os.walk(args.tables):
for spec_file in files:
@ -138,6 +146,8 @@ def main(argc, argv):
table_spec = gen_spec(tree)
table_profile = profile.get("%s.%s" % (platform, name), {})
table_spec["profile"] = NoIndent(table_profile)
table_spec["blacklisted"] = is_blacklisted(table_spec["name"],
blacklist=blacklist)
categories[platform]["tables"].append(table_spec)
categories = [{"key": k, "name": v["name"], "tables": v["tables"]}
for k, v in categories.iteritems()]

View File

@ -22,16 +22,18 @@ LOG_FORMAT = "%(levelname)s [Line %(lineno)d]: %(message)s"
TEMPLATES = {}
# Temporary reserved column names
RESERVED = ["group"]
RESERVED = ["n"]
# Supported SQL types for spec
class DataType(object):
def __init__(self, affinity, cpp_type="std::string"):
'''A column datatype is a pair of a SQL affinity to C++ type.'''
self.affinity = affinity
self.type = cpp_type
def __repr__(self):
return self.affinity
# Define column-type MACROs for the table specs
TEXT = DataType("TEXT")
DATE = DataType("TEXT")
DATETIME = DataType("TEXT")
@ -50,22 +52,22 @@ def to_camel_case(snake_case):
def lightred(msg):
return "\033[1;31m %s \033[0m" % str(msg)
def is_blacklisted(path, table_name):
def is_blacklisted(table_name, path=None, blacklist=None):
"""Allow blacklisting by tablename."""
specs_path = os.path.dirname(os.path.dirname(path))
blacklist_path = os.path.join(specs_path, "blacklist")
if not os.path.exists(blacklist_path):
return False
try:
with open(blacklist_path, "r") as fh:
blacklist = [line.strip() for line in fh.read().split("\n")
if len(line.strip()) > 0 and line.strip()[0] != "#"]
if table_name in blacklist:
return True
except:
# Blacklist is not readable.
pass
return False
if blacklist is None:
specs_path = os.path.dirname(os.path.dirname(path))
blacklist_path = os.path.join(specs_path, "blacklist")
if not os.path.exists(blacklist_path):
return False
try:
with open(blacklist_path, "r") as fh:
blacklist = [line.strip() for line in fh.read().split("\n")
if len(line.strip()) > 0 and line.strip()[0] != "#"]
except:
# Blacklist is not readable.
return False
# table_name based blacklisting!
return table_name in blacklist if blacklist else False
def setup_templates(path):
tables_path = os.path.dirname(os.path.dirname(os.path.dirname(path)))
@ -237,7 +239,8 @@ def main(argc, argv):
with open(filename, "rU") as file_handle:
tree = ast.parse(file_handle.read())
exec(compile(tree, "<string>", "exec"))
if not disable_blacklist and is_blacklisted(filename, table.table_name):
blacklisted = is_blacklisted(table.table_name, path=filename)
if not disable_blacklist and blacklisted:
table.blacklist(output)
else:
table.generate(output)

View File

@ -3,6 +3,7 @@
set -e
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
BUILD_DIR="$SCRIPT_DIR/../build"
WORKING_DIR="$SCRIPT_DIR/../.sources"
export PATH="$PATH:/usr/local/bin"
@ -243,6 +244,9 @@ function check() {
if [[ "$1" = "build" ]]; then
echo $HASH > "$2/.provision"
if [[ ! -z "$SUDO_USER" ]]; then
chown $SUDO_USER "$2/.provision"
fi
return
elif [[ ! "$1" = "check" ]]; then
return
@ -271,6 +275,10 @@ function main() {
fi
mkdir -p "$WORKING_DIR"
if [[ ! -z "$SUDO_USER" ]]; then
chown -R $SUDO_USER "$BUILD_DIR"
chown -R $SUDO_USER "$WORKING_DIR"
fi
cd "$WORKING_DIR"
if [[ $OS = "centos" ]]; then