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
15 changed files with 327 additions and 16 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.5], [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 -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);
+47 -7
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",
+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