Compare commits

..
Author SHA1 Message Date
Alex Jaramillo d866f468f8 Local Makefile
Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2025-03-21 13:47:54 -07:00
Alex Jaramillo 07d334c718 Fizzing iorecord.c
Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2025-03-21 13:45:44 -07:00
Alex Jaramillo a2367132bd Fuzzing configuration.c
Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2025-03-21 13:13:10 -07:00
Alex Jaramillo ed42b6ed3f Fuzzing libtelemetry
Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2025-03-21 10:02:13 -07:00
Alex Jaramillo d162311e31 telemetry.c: improvements
- tm_set_payload: check payload for NULL value before duplication, this
  is desirable due the use of strnlen and strndup.

- tm_set_payload: replace strlen with strnlen to use MAX_PAYLOAD_LENGTH
  during function call instead of checking after the fact.

Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2025-03-19 15:35:45 -07:00
Alex Jaramillo f23bc1ae12 configure.ac: bump version
Replace clear text record format with json to transmit records to
telemetry collector.

Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2025-03-13 18:32:18 -07:00
Brett T. Warden d0ee68ba43 Publish a JSON message to telemetry server
Replace the custom HTTP headers and plain text payload with a JSON
object. This is the new v3 API implementation.
2025-03-10 12:06:12 -07:00
Brett T. Warden 9bb9288153 Link telemetry post daemon against json-c
also link its test
2025-03-10 12:06:12 -07:00
Brett T. Warden b9ec06fe9a Check for json-c
json-c is now required, in order to construct the JSON payload.
2025-03-10 12:06:12 -07:00
Brett T. Warden 32b306daa9 Update default URL API to v3
Updating the HTTP API from v2 to v3 to support JSON message
encapsulation instead of custom HTTP headers with a single-field POST
field for the payload.
2025-03-10 12:06:12 -07:00
Alex Jaramillo c3b8d81b6d configure.ac: bump version
Increase the version to 2.3.5 to release fix for missing records in
journal and preempt klogscanner service start inside containers.

Signed-off-by: Alex Jaramillo <alex.v.jaramillo@intel.com>
2020-02-24 21:37:06 +00:00
Alex Jaramillo f4f012af7b fix first record missing from journal
This change fixes the condition when the first record after telempostd
starts is not inserted in the telemetry journal.

Notice! there is a change in the logic, while previously records
where inserted to juornal as soon the record was processed. After this
change records will be inserted in the journal only when successfully
delivered or record_server_delivery_enabled is set to false.
2020-02-24 13:27:32 -08:00
Thiago Macieira 9db4e1d8c9 Don't try to start the klogscanner service inside a container
We can't access the klog.

● klogscanner.service - Telemetrics Kernel Log Scanner
     Loaded: loaded (/usr/lib/systemd/system/klogscanner.service; disabled; vendor preset: disabled)
     Active: failed (Result: exit-code) since Fri 2020-01-31 10:44:58 PST; 8min ago
    Process: 21 ExecStart=/usr/bin/klogscanner (code=exited, status=1/FAILURE)
   Main PID: 21 (code=exited, status=1/FAILURE)

Jan 31 10:44:58 <MACHINENAME> systemd[1]: Started Telemetrics Kernel Log Scanner.
Jan 31 10:44:58 <MACHINENAME> klogscanner[21]: ERROR: Cannot read size of kernel ring buffer: Operation not permitted
Jan 31 10:44:58 <MACHINENAME> systemd[1]: klogscanner.service: Main process exited, code=exited, status=1/FAILURE
Jan 31 10:44:58 <MACHINENAME> systemd[1]: klogscanner.service: Failed with result 'exit-code'.
2020-02-05 10:06:08 -08:00
18 changed files with 349 additions and 35 deletions
+1
View File
@@ -21,6 +21,7 @@ include $(top_srcdir)/src/nica/local.mk
include $(top_srcdir)/src/probes/local.mk
include $(top_srcdir)/src/journal/local.mk
include $(top_srcdir)/tests/local.mk
include $(top_srcdir)/fuzz/local.mk
release:
@git rev-parse v$(PACKAGE_VERSION) &> /dev/null; \
+5 -3
View File
@@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69])
AC_INIT([telemetrics-client], [2.3.4], [https://clearlinux.org/])
AC_INIT([telemetrics-client], [2.4.0], [https://clearlinux.org/])
AC_CONFIG_AUX_DIR([build-aux])
AM_INIT_AUTOMAKE([1.14 -Wall -Werror -Wno-extra-portability foreign subdir-objects])
AM_SILENT_RULES([yes])
@@ -33,6 +33,7 @@ AX_VALGRIND_CHECK
# check >= 0.9.12 is required for TAP output
PKG_CHECK_MODULES([CHECK], [check >= 0.12])
PKG_CHECK_MODULES([CURL], [libcurl])
PKG_CHECK_MODULES([JSON_C], [json-c])
AC_CHECK_LIB([elf], [elf_begin], [have_elflib=yes], [AC_MSG_ERROR([Unable to find libelf from elfutils])])
AC_CHECK_LIB([dw], [dwfl_begin], [have_dwlib=yes], [AC_MSG_ERROR([Unable to find libdw from elfutils])])
AS_IF([test "x$have_elflib" = "xyes" -a "x$have_dwlib" = "xyes"],
@@ -49,6 +50,7 @@ AC_CHECK_HEADERS([elfutils/libdwfl.h])
AC_CHECK_HEADERS([fcntl.h])
AC_CHECK_HEADERS([getopt.h])
AC_CHECK_HEADERS([inttypes.h])
AC_CHECK_HEADERS([json-c/json.h])
AC_CHECK_HEADERS([libelf.h])
AC_CHECK_HEADERS([limits.h])
AC_CHECK_HEADERS([stdarg.h])
@@ -128,8 +130,8 @@ AC_ARG_WITH([loglevel], AS_HELP_STRING([--with-loglevel=NUM],
AC_DEFINE_UNQUOTED([MAX_LOG_LEVEL], [${loglevel}], [Maximum log level for binaries])
AC_ARG_WITH([backendserveraddr], AS_HELP_STRING([--with-backendserveraddr=URI],
[uri to telemetrics backend server @<:@default=https://clr.telemetry.intel.com/v2/collector@:>@]), [backendaddr=${withval}])
test -z "${backendaddr}" && backendaddr=https://clr.telemetry.intel.com/v2/collector
[uri to telemetrics backend server @<:@default=https://clr.telemetry.intel.com/v3/collector@:>@]), [backendaddr=${withval}])
test -z "${backendaddr}" && backendaddr=https://clr.telemetry.intel.com/v3/collector
AC_SUBST(BACKEND_ADDR, [${backendaddr}])
AC_ARG_ENABLE([logtype], AS_HELP_STRING([--enable-logtype],
+3
View File
@@ -0,0 +1,3 @@
fuzz_libtelem
fuzz_config
fuzz_iorec
+54
View File
@@ -0,0 +1,54 @@
bin_PROGRAMS = fuzz_libtelem fuzz_config fuzz_iorec
CC = clang
CFLAGS = \
-g \
-I../. \
-I../src/. \
-DLOCALSTATEDIR=\"/tmp\" \
-DDATADIR="" \
-DBACKEND_ADDR=\"abcde\"
# Define the source files for each binary
fuzz_libtelem_SOURCES = \
fuzz_libtelem.c
fuzz_config_SOURCES = \
../src/configuration.c \
../src/nica/hashmap.c \
../src/nica/inifile.c \
utils.c \
fuzz_config.c
fuzz_iorec_SOURCES = \
utils.c \
fuzz_iorec.c
fuzz_iorec_CFLAGS = \
$(CFLAGS) \
-fsanitize=address,fuzzer
fuzz_libtelem_CFLAGS = \
$(CFLAGS) \
-fsanitize=address,fuzzer \
-ltelemetry
fuzz_config_CFLAGS = \
$(CFLAGS) \
-fsanitize=fuzzer # There's a false positive in configuration.c:189:56
all: $(bin_PROGRAMS)
fuzz_libtelem: $(fuzz_libtelem_SOURCES)
$(CC) $(fuzz_libtelem_CFLAGS) -o $@ $^
fuzz_config: $(fuzz_config_SOURCES)
$(CC) $(fuzz_config_CFLAGS) -o $@ $^
fuzz_iorec: $(fuzz_iorec_SOURCES)
$(CC) $(fuzz_iorec_CFLAGS) -o $@ $^
clean:
rm -f $(bin_PROGRAMS) *.o
.PHONY: all clean
+21
View File
@@ -0,0 +1,21 @@
# Fuzzing telemetrics-client
## Build
```
make
```
## Run
```
#fuzz_config
fuzz_config -close_fd_mask=2
#fuzz_libtelem
fuzz_libtelem
```
# Link runs in background
```
sudo systemd-run --unit=fuzzing.service -r --uid=$(id -u) \
--working-directory=${PWD}/fuzz -E LD_LIBRARY_PATH=${PWD}/src/.libs/ fuzz/<fuzzer_> -max_total_time=86400 -runs=10000000 fuzz/corpus
```
+32
View File
@@ -0,0 +1,32 @@
#include <configuration.h>
#include <string.h>
#include <unistd.h>
#include "utils.h"
void test_configuration(const uint8_t *data, size_t size) {
char content[16384];
char config_file[] = "/tmp/config_fuzzerXXXXXX";
static struct configuration config = { { 0 }, { 0 }, { 0 }, false, NULL };
// Get a null ended string
strncpy(content, (const char *)data, size);
save_data(config_file, content);
read_config_from_file(config_file, &config);
for (int i = 0; i < 6; i++) {
if (config.strValues[i] == NULL) {
continue;
}
}
unlink(config_file);
}
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
test_configuration(data, size);
return 0;
}
+28
View File
@@ -0,0 +1,28 @@
#include <configuration.h>
#include <unistd.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "utils.h"
void test_read_record(const uint8_t *data, size_t size) {
char content[16384];
char record_file[] = "/tmp/record_fuzzerXXXXXX";
static struct configuration config = { { 0 }, { 0 }, { 0 }, false, NULL };
// Get a null ended string
strncpy(content, (const char *)data, size);
save_data(record_file, content);
// read_record(char *fullpath, char *headers[], char **body, char **cfg_file)
unlink(record_file);
}
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
test_read_record(data, size);
return 0;
}
+73
View File
@@ -0,0 +1,73 @@
#include <telemetry.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int test_tm_set_payload(char *payload) {
int ret = 0;
static uint32_t severity = 1;
static uint32_t payload_version = 1;
static const char *opt_class = "clearlinux/debug/fuzzing";
struct telem_ref *t_ref = NULL;
if ((ret = tm_create_record(&t_ref, (uint32_t)severity,
(char *)opt_class, payload_version)) < 0) {
return ret;
}
ret = tm_set_payload(t_ref, payload);
tm_free_record(t_ref);
return ret;
}
void test_tm_set_config_file(char *path) {
/*
No need to check returning value because
the important part is not it fails, but
if this crashes.
*/
tm_set_config_file(path);
}
int test_tm_set_event_id(char *full_id) {
int ret = 0;
char *id = NULL;
static uint32_t severity = 1;
static uint32_t payload_version = 1;
static const char *opt_class = "clearlinux/debug/fuzzing";
struct telem_ref *t_ref = NULL;
if ((ret = tm_create_record(&t_ref, (uint32_t)severity,
(char *)opt_class, payload_version)) < 0) {
return ret;
}
id = strndup(full_id, 32);
ret = tm_set_event_id(t_ref, id);
free(id);
tm_free_record(t_ref);
return ret;
}
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
int ret = 0;
char *payload = NULL;
/*
tm_set_payload expects a null terminated string, this
step duplicates data into a null terminated string.
*/
payload = strndup((char *)data, size);
if (payload == NULL) {
goto end;
}
test_tm_set_payload(payload);
test_tm_set_config_file(payload);
test_tm_set_event_id(payload);
free(payload);
end:
return ret;
}
+30
View File
@@ -0,0 +1,30 @@
COMPILER = clang
CLANG_FLAGS = \
-g \
-fsanitize=address,fuzzer
SOURCES = fuzz/libtelem_fuzzer.c
BIN = fuzz/libtelem_fuzzer
HALF_DAY_SEC = 43200
FUZZ_FLAGS = \
-max_len=16384 \
-timeout=10 \
-only_ascii=1 \
-runs=1000000 \
-max_total_time=$(HALF_DAY_SEC)
build_fuzz_libtelem:
LD_LIBRARY_PATH=src/.libs $(COMPILER) $(CLANG_FLAGS) -ltelemetry $(SOURCES) -o $(BIN)
fuzz: build_fuzz_kibtelem
$(BIN) fuzz/corpus $(FUZZ_FLAGS)
# vim: filetype=automake tabstop=8 shiftwidth=8 noexpandtab
create a makefile that builds {3 binaries}
{4 binaries}: fuzz_libtelem fuzz_config fuzz_iorec
each one has different compiling flags and SOURCES
+24
View File
@@ -0,0 +1,24 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int save_data(char *filename, const char *data) {
FILE *temp_file = NULL;
int fd;
fd = mkstemp(filename);
if (fd == -1) {
return 1;
}
temp_file = fdopen(fd, "w");
if (temp_file == NULL) {
close(fd);
return 1;
}
fprintf(temp_file, "%s", data);
fclose(temp_file);
return 0;
}
+1
View File
@@ -0,0 +1 @@
int save_data(char *filename, const char *data);
+1
View File
@@ -1,5 +1,6 @@
[Unit]
Description=Telemetrics Kernel Log Scanner
ConditionVirtualization=!container
Requires=telemprobd.socket
After=telemprobd.socket
+1 -1
View File
@@ -61,7 +61,7 @@ endif
%D%/iorecord.c \
%D%/iorecord.h
%C%_telempostd_LDADD = $(CURL_LIBS) \
%C%_telempostd_LDADD = $(CURL_LIBS) $(JSON_C_LIBS) \
%D%/libtelem-shared.la \
%D%/libtelemetry.la
+6 -5
View File
@@ -977,17 +977,18 @@ int tm_set_payload(struct telem_ref *t_ref, char *payload)
size_t payload_len;
int ret = 0;
payload_len = strlen((char *)payload);
if (payload_len > MAX_PAYLOAD_LENGTH) {
if (payload == NULL) {
telem_log(LOG_WARNING, "payload pointer is NULL\n");
return -EINVAL;
}
payload_len = strnlen(payload, MAX_PAYLOAD_LENGTH);
if (payload_is_ascii(payload, payload_len) != 0) {
return -EINVAL;
}
t_ref->record->payload = strdup(payload);
t_ref->record->payload = strndup(payload, payload_len);
if (!t_ref->record->payload) {
telem_log(LOG_CRIT, "CRIT: Out of memory\n");
@@ -1273,7 +1274,7 @@ int tm_send_record(struct telem_ref *t_ref)
*/
record_size = (2 * sizeof(uint32_t)) + total_size + 1;
data = calloc(sizeof(char), record_size);
data = (char *)calloc(sizeof(char), record_size);
if (!data) {
telem_log(LOG_CRIT, "CRIT: Out of memory\n");
close(sfd);
+62 -19
View File
@@ -1,7 +1,7 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2018 Intel Corporation
* Copyright 2018-2023 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it under
* the terms and conditions of the GNU Lesser General Public License, as
@@ -25,6 +25,7 @@
#include <stdbool.h>
#include <sys/stat.h>
#include <curl/curl.h>
#include <json-c/json.h>
#include <sys/signalfd.h>
#include "log.h"
@@ -210,13 +211,47 @@ size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)
return size * nmemb;
}
char *create_json_message(char *tm_headers[], char *tm_payload)
{
/*
* Embed the telemetry record headers and the telemetry payload into a
* JSON object string.
*/
char *json_string = NULL;
json_object *root = json_object_new_object();
/* Add the telemetry record headers */
for (int i = 0; i < NUM_HEADERS; i++) {
/* ex: arch: x86_64 */
/* Split the header field into name/value */
strtok(tm_headers[i], ":");
json_object *value = json_object_new_string(strtok(NULL, " "));
json_object_object_add(root, tm_headers[i], value);
}
json_object *payload = json_object_new_string(tm_payload);
json_object_object_add(root, "payload", payload);
/* Keep our own copy of the json string */
json_string = strdup(json_object_to_json_string_ext(root,
JSON_C_TO_STRING_PLAIN |
JSON_C_TO_STRING_NOSLASHESCAPE));
/* Free the memory associated with the JSON objects */
json_object_put(root);
return json_string;
}
bool post_record_http(char *headers[], char *body, char *cfg)
{
CURL *curl;
int res = 0;
char *content = "Content-Type: application/text";
char *content = "Content-Type: application/json";
struct curl_slist *custom_headers = NULL;
char errorbuf[CURL_ERROR_SIZE];
char *json_body = NULL;
long http_response = 0;
const char *cert_file = get_cainfo_config();
const char *tid_header = get_tidheader_config();
@@ -237,6 +272,9 @@ bool post_record_http(char *headers[], char *body, char *cfg)
telem_debug("DEBUG: override server_addr:%s\n", server_addr_config());
}
// Generate the JSON message body
json_body = create_json_message(headers, body);
// Initialize the libcurl global environment once per POST. This lets us
// clean up the environment after each POST so that when the daemon is
// sitting idle, it will be consuming as little memory as possible.
@@ -264,16 +302,13 @@ bool post_record_http(char *headers[], char *body, char *cfg)
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
for (int i = 0; i < NUM_HEADERS; i++) {
custom_headers = curl_slist_append(custom_headers, headers[i]);
}
custom_headers = curl_slist_append(custom_headers, tid_header);
// This should be set by probes/libtelemetry in the future
custom_headers = curl_slist_append(custom_headers, content);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, custom_headers);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body);
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, strlen(body));
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_body);
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, strlen(json_body));
curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_TRY);
if (strlen(cert_file) > 0) {
@@ -315,6 +350,11 @@ bool post_record_http(char *headers[], char *body, char *cfg)
curl_global_cleanup();
Done:
if (json_body) {
free(json_body);
json_body = NULL;
}
if (saved_config_file != NULL) {
if (set_config_file(saved_config_file) != 0) {
telem_log(LOG_ERR, "set-config_file(): Failed to set %s",
@@ -490,7 +530,7 @@ static bool deliver_record(TelemPostDaemon *daemon, char *headers[], char *body,
return ret;
}
bool process_staged_record(char *filename, bool is_retry, TelemPostDaemon *daemon)
bool process_staged_record(char *filename, TelemPostDaemon *daemon)
{
int k;
bool ret = false;
@@ -530,20 +570,12 @@ bool process_staged_record(char *filename, bool is_retry, TelemPostDaemon *daemo
goto end_processing_file;
}
/* Retries should not be recorded */
if (is_retry == false) {
/** Journal entry **/
save_entry_to_journal(daemon, current_time, headers);
/** Record retention **/
apply_retention_policies(daemon, body);
}
/** Record delivery **/
if (!daemon->record_server_delivery_enabled) {
telem_log(LOG_INFO, "record server delivery disabled\n");
// Not an error condition
ret = true;
goto end_processing_file;
goto end_record_delivery;
}
/** Spool policies **/
@@ -571,6 +603,17 @@ bool process_staged_record(char *filename, bool is_retry, TelemPostDaemon *daemo
/** Deliver or spool **/
ret = deliver_record(daemon, headers, body, cfg_file);
end_record_delivery:
/** Save record once it is properly delivered, if record
* is spooled the record is not saved to journal until
* delievered on a re-try **/
if (ret) {
/** Save to journal **/
save_entry_to_journal(daemon, current_time, headers);
/** Record retention **/
apply_retention_policies(daemon, body);
}
end_processing_file:
/** Update spool size if record will be removed **/
if (ret) {
@@ -625,7 +668,7 @@ int staging_records_loop(TelemPostDaemon *daemon)
telem_log(LOG_ERR, "Failed to allocate memory for staging record full path\n");
exit(EXIT_FAILURE);
}
if (process_staged_record(record_path, true, daemon)) {
if (process_staged_record(record_path, daemon)) {
unlink(record_path);
processed++;
}
@@ -721,7 +764,7 @@ void run_daemon(TelemPostDaemon *daemon)
exit(EXIT_FAILURE);
}
/* Process inotify event */
if (process_staged_record(record_name, false, daemon)) {
if (process_staged_record(record_name, daemon)) {
unlink(record_name);
}
free(record_name);
+2 -3
View File
@@ -85,11 +85,9 @@ void close_daemon(TelemPostDaemon *daemon);
* Processed record written on disk
*
* @param filename a pointor to record on disk
* @param is_retry a boolean value that indicates if
* the record has been previously processed.
* @param daemon post to telemetry post daemon
*/
bool process_staged_record(char *filename, bool is_retry, TelemPostDaemon *daemon);
bool process_staged_record(char *filename, TelemPostDaemon *daemon);
/**
* Scans staging directory to process files that were
@@ -107,6 +105,7 @@ int staging_records_loop(TelemPostDaemon *daemon);
* @param body a pointer to the payload
* @param cfg_file a pointer to a non-default configuration
* file to be used.
* @return true if successful, false otherwise
*/
bool post_record_http(char *headers[], char *body, char *cfg_file);
+4 -4
View File
@@ -62,7 +62,7 @@ START_TEST(check_handle_client_with_no_data)
bool success;
char *filename = ABSTOPSRCDIR "/tests/telempostd/empty_message";
success = process_staged_record(filename, false, &tdaemon);
success = process_staged_record(filename, &tdaemon);
// Return true to remove corrupted record
ck_assert(success == true);
}
@@ -75,7 +75,7 @@ START_TEST(check_handle_client_with_incorrect_data)
bool success;
char *filename = ABSTOPSRCDIR "/tests/telempostd/incorrect_message";
success = process_staged_record(filename, false, &tdaemon);
success = process_staged_record(filename, &tdaemon);
// Return true to remove corrupted record
ck_assert(success == true);
}
@@ -88,7 +88,7 @@ START_TEST(check_process_record_with_correct_size_and_data)
bool success;
char *filename = ABSTOPSRCDIR "/tests/telempostd/correct_message";
success = process_staged_record(filename, false, &tdaemon);
success = process_staged_record(filename, &tdaemon);
ck_assert(success == true);
}
END_TEST
@@ -100,7 +100,7 @@ START_TEST(check_process_record_with_incorrect_headers)
bool success;
char *filename = ABSTOPSRCDIR "/tests/telempostd/incorrect_headers";
success = process_staged_record(filename, false, &tdaemon);
success = process_staged_record(filename, &tdaemon);
// Return true to remove corrupted record
ck_assert(success == true);
}
+1
View File
@@ -93,6 +93,7 @@ EXTRA_DIST += \
%C%_check_postd_LDADD = \
@CHECK_LIBS@ \
@CURL_LIBS@ \
@JSON_C_LIBS@ \
$(top_builddir)/src/libtelem-shared.la
if LOG_SYSTEMD