<commit_before>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2011 Couchbase, Inc.
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <sigar.h>

#ifdef _WIN32
#include <io.h>
#include <fcntl.h>
#endif

#define DEFAULT(value, def) ((value) == SIGAR_FIELD_NOTIMPL ? (def) : (value))
#define MUST_SUCCEED(body)                      \
    do {                                        \
        int _ret = (body);                      \
        if (_ret != SIGAR_OK) {                 \
            exit(1);                            \
        }                                       \
    } while (0)

#define NUM_INTERESTING_PROCS 40
#define PROCS_REFRESH_INTERVAL 20
#define PROC_NAME_LEN 60

struct proc {
    sigar_pid_t pid;
    sigar_pid_t ppid;
    sigar_uint64_t start_time;
    char name[PROC_NAME_LEN];
};

struct proc_stats {
    char name[PROC_NAME_LEN];
    uint32_t cpu_utilization;

    uint64_t pid;
    uint64_t ppid;

    uint64_t mem_size;
    uint64_t mem_resident;
    uint64_t mem_share;
    uint64_t minor_faults;
    uint64_t major_faults;
    uint64_t page_faults;
};

struct system_stats {
    uint32_t version;
    uint32_t struct_size;

    uint64_t cpu_total_ms;
    uint64_t cpu_idle_ms;

    uint64_t swap_total;
    uint64_t swap_used;
    uint64_t swap_page_in;
    uint64_t swap_page_out;

    uint64_t mem_total;
    uint64_t mem_used;
    uint64_t mem_actual_used;
    uint64_t mem_actual_free;

    struct proc_stats interesting_procs[NUM_INTERESTING_PROCS];
};

static int is_interesting_process(const char *name)
{
    return (strcmp(name, "moxi") != 0 &&
            strcmp(name, "inet_gethost") != 0 &&
            strcmp(name, "memsup") != 0 &&
            strcmp(name, "cpu_sup") != 0 &&
            strcmp(name, "sh") != 0 &&
            strcmp(name, "epmd") != 0);
}

static int proc_ppid_compare(const void *va, const void *vb)
{
    const struct proc *a = va;
    const struct proc *b = vb;

    if (a->ppid < b->ppid) {
        return -1;
    } else if (a->ppid > b->ppid) {
        return 1;
    } else {
        return 0;
    }
}

static int find_interesting_procs(sigar_t *sigar, sigar_pid_t babysitter_pid,
                                  struct proc interesting_procs[NUM_INTERESTING_PROCS])
{
    unsigned long i;
    int interesting_count = 0;

    sigar_proc_list_t proc_list;

    struct proc *procs;
    struct proc *procs_end;

    struct proc *babysitter_proc = NULL;

    MUST_SUCCEED(sigar_proc_list_get(sigar, &proc_list));

    if (proc_list.number == 0) {
        goto find_interesting_procs_return;
    }

    procs = malloc(sizeof(struct proc) * proc_list.number);
    if (procs == NULL) {
        exit(1);
    }

    procs_end = procs;

    for (i = 0; i < proc_list.number; ++i) {
        sigar_pid_t pid = proc_list.data[i];

        sigar_proc_state_t proc_state;
        sigar_proc_cpu_t proc_cpu;

        if (sigar_proc_state_get(sigar, pid, &proc_state) != SIGAR_OK) {
            continue;
        }

        if (sigar_proc_cpu_get(sigar, pid, &proc_cpu) != SIGAR_OK) {
            continue;
        }

        procs_end->pid = pid;
        procs_end->ppid = proc_state.ppid;
        procs_end->start_time = proc_cpu.start_time;
        strncpy(procs_end->name, proc_state.name, PROC_NAME_LEN);

        if (pid == babysitter_pid) {
            babysitter_proc = procs_end;
        }

        ++procs_end;
    }

    // something went utterly wrong, we couldn't find babysitter
    if (!babysitter_proc) {
        exit(1);
    }

    interesting_procs[interesting_count++] = *babysitter_proc;

    qsort(procs, procs_end - procs, sizeof(struct proc), proc_ppid_compare);

    for (i = 0; i < interesting_count; ++i) {
        sigar_pid_t ppid = interesting_procs[i].pid;
        struct proc key = {0};
        struct proc *child;

        key.ppid = ppid;

        child = bsearch(&key, procs, procs_end - procs, sizeof(struct proc),
                        proc_ppid_compare);
        if (!child) {
            continue;
        }

        // which element of multiple equal elements returned is unspecified;
        // so we need to check neighboring elements in both directions
        while (child > procs && (child - 1)->ppid == ppid) {
            --child;
        }

        while (child->ppid == ppid && child < procs_end) {
            if (is_interesting_process(child->name) &&
                interesting_count < NUM_INTERESTING_PROCS) {

                interesting_procs[interesting_count++] = *child;
            }

            ++child;
        }
    }

    free(procs);

find_interesting_procs_return:
    MUST_SUCCEED(sigar_proc_list_destroy(sigar, &proc_list));
<commit_msg>MB-32205: Fix heap-buffer-overflow in find_interesting_procs<commit_after>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2011 Couchbase, Inc.
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <sigar.h>

#ifdef _WIN32
#include <io.h>
#include <fcntl.h>
#endif

#define DEFAULT(value, def) ((value) == SIGAR_FIELD_NOTIMPL ? (def) : (value))
#define MUST_SUCCEED(body)                      \
    do {                                        \
        int _ret = (body);                      \
        if (_ret != SIGAR_OK) {                 \
            exit(1);                            \
        }                                       \
    } while (0)

#define NUM_INTERESTING_PROCS 40
#define PROCS_REFRESH_INTERVAL 20
#define PROC_NAME_LEN 60

struct proc {
    sigar_pid_t pid;
    sigar_pid_t ppid;
    sigar_uint64_t start_time;
    char name[PROC_NAME_LEN];
};

struct proc_stats {
    char name[PROC_NAME_LEN];
    uint32_t cpu_utilization;

    uint64_t pid;
    uint64_t ppid;

    uint64_t mem_size;
    uint64_t mem_resident;
    uint64_t mem_share;
    uint64_t minor_faults;
    uint64_t major_faults;
    uint64_t page_faults;
};

struct system_stats {
    uint32_t version;
    uint32_t struct_size;

    uint64_t cpu_total_ms;
    uint64_t cpu_idle_ms;

    uint64_t swap_total;
    uint64_t swap_used;
    uint64_t swap_page_in;
    uint64_t swap_page_out;

    uint64_t mem_total;
    uint64_t mem_used;
    uint64_t mem_actual_used;
    uint64_t mem_actual_free;

    struct proc_stats interesting_procs[NUM_INTERESTING_PROCS];
};

static int is_interesting_process(const char *name)
{
    return (strcmp(name, "moxi") != 0 &&
            strcmp(name, "inet_gethost") != 0 &&
            strcmp(name, "memsup") != 0 &&
            strcmp(name, "cpu_sup") != 0 &&
            strcmp(name, "sh") != 0 &&
            strcmp(name, "epmd") != 0);
}

static int proc_ppid_compare(const void *va, const void *vb)
{
    const struct proc *a = va;
    const struct proc *b = vb;

    if (a->ppid < b->ppid) {
        return -1;
    } else if (a->ppid > b->ppid) {
        return 1;
    } else {
        return 0;
    }
}

static int find_interesting_procs(sigar_t *sigar, sigar_pid_t babysitter_pid,
                                  struct proc interesting_procs[NUM_INTERESTING_PROCS])
{
    unsigned long i;
    int interesting_count = 0;

    sigar_proc_list_t proc_list;

    struct proc *procs;
    struct proc *procs_end;

    struct proc *babysitter_proc = NULL;

    MUST_SUCCEED(sigar_proc_list_get(sigar, &proc_list));

    if (proc_list.number == 0) {
        goto find_interesting_procs_return;
    }

    procs = malloc(sizeof(struct proc) * proc_list.number);
    if (procs == NULL) {
        exit(1);
    }

    procs_end = procs;

    for (i = 0; i < proc_list.number; ++i) {
        sigar_pid_t pid = proc_list.data[i];

        sigar_proc_state_t proc_state;
        sigar_proc_cpu_t proc_cpu;

        if (sigar_proc_state_get(sigar, pid, &proc_state) != SIGAR_OK) {
            continue;
        }

        if (sigar_proc_cpu_get(sigar, pid, &proc_cpu) != SIGAR_OK) {
            continue;
        }

        procs_end->pid = pid;
        procs_end->ppid = proc_state.ppid;
        procs_end->start_time = proc_cpu.start_time;
        strncpy(procs_end->name, proc_state.name, PROC_NAME_LEN);

        if (pid == babysitter_pid) {
            babysitter_proc = procs_end;
        }

        ++procs_end;
    }

    // something went utterly wrong, we couldn't find babysitter
    if (!babysitter_proc) {
        exit(1);
    }

    interesting_procs[interesting_count++] = *babysitter_proc;

    qsort(procs, procs_end - procs, sizeof(struct proc), proc_ppid_compare);

    for (i = 0; i < interesting_count; ++i) {
        sigar_pid_t ppid = interesting_procs[i].pid;
        struct proc key = {0};
        struct proc *child;

        key.ppid = ppid;

        child = bsearch(&key, procs, procs_end - procs, sizeof(struct proc),
                        proc_ppid_compare);
        if (!child) {
            continue;
        }

        // which element of multiple equal elements returned is unspecified;
        // so we need to check neighboring elements in both directions
        while (child > procs && (child - 1)->ppid == ppid) {
            --child;
        }

        while (child < procs_end && child->ppid == ppid) {
            if (is_interesting_process(child->name) &&
                interesting_count < NUM_INTERESTING_PROCS) {

                interesting_procs[interesting_count++] = *child;
            }

            ++child;
        }
    }

    free(procs);

find_interesting_procs_return:
    MUST_SUCCEED(sigar_proc_list_destroy(sigar, &proc_list));

{'fraction_non_alphanumeric': 0.10245404497527293, 'fraction_numerical': 0.015582719044508725, 'mean_word_length': 2.228313253012048, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2319628', 'n_tokens_mistral': 3780, 'n_tokens_neox': 3561, 'n_words': 858}