Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F175460466
D60564.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
19 KB
Referenced Files
None
Subscribers
None
D60564.diff
View Options
diff --git a/lib/libpmc/libpmc_json.cc b/lib/libpmc/libpmc_json.cc
--- a/lib/libpmc/libpmc_json.cc
+++ b/lib/libpmc/libpmc_json.cc
@@ -26,6 +26,7 @@
*
*/
+#include <sys/param.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <assert.h>
@@ -256,7 +257,7 @@
{
char eventbuf[1024];
string result;
- uint32_t i;
+ uint32_t i, start;
string startent;
startent = startentry(ev);
@@ -266,12 +267,20 @@
startent.c_str(), ev->pl_u.pl_cc.pl_pmcid, ev->pl_u.pl_cc.pl_pid,
ev->pl_u.pl_cc.pl_tid, ev->pl_u.pl_cc.pl_cpuflags, ev->pl_u.pl_cc.pl_cpuflags2);
result = string(eventbuf);
- for (i = 0; i < ev->pl_u.pl_cc.pl_npc - 1; i++) {
+ start = 0;
+ if ((ev->pl_u.pl_cc.pl_cpuflags & PMC_CC_F_MULTIPART) != 0 &&
+ !pmclog_multipart_callchain_offset(ev->pl_u.pl_cc.pl_pc,
+ ev->pl_u.pl_cc.pl_npc, &start))
+ start = ev->pl_u.pl_cc.pl_npc;
+ for (i = start; i + 1 < ev->pl_u.pl_cc.pl_npc; i++) {
snprintf(eventbuf, sizeof(eventbuf), "\"0x%016jx\", ", (uintmax_t)ev->pl_u.pl_cc.pl_pc[i]);
result += string(eventbuf);
}
- snprintf(eventbuf, sizeof(eventbuf), "\"0x%016jx\"]}\n", (uintmax_t)ev->pl_u.pl_cc.pl_pc[i]);
- result += string(eventbuf);
+ if (i < ev->pl_u.pl_cc.pl_npc) {
+ snprintf(eventbuf, sizeof(eventbuf), "\"0x%016jx\"]}\n", (uintmax_t)ev->pl_u.pl_cc.pl_pc[i]);
+ result += string(eventbuf);
+ } else
+ result += string("]}\n");
return (result);
}
diff --git a/lib/libpmc/pmc.ibs.3 b/lib/libpmc/pmc.ibs.3
--- a/lib/libpmc/pmc.ibs.3
+++ b/lib/libpmc/pmc.ibs.3
@@ -48,6 +48,9 @@
contained in the callchain.
The first 8 bytes of the callchain contain four tuples with a one byte type and
a one byte length field.
+Length fields are expressed in native pointer-sized callchain slots, not in
+64-bit payload words; therefore each 64-bit IBS payload entry consumes one slot
+on 64-bit platforms and two slots on 32-bit platforms.
The regular PMC callchain can be found following the multipart payload.
.Pp
IBS only provides two events that analyze instruction fetches and instruction
diff --git a/lib/libpmc/pmclog.c b/lib/libpmc/pmclog.c
--- a/lib/libpmc/pmclog.c
+++ b/lib/libpmc/pmclog.c
@@ -150,7 +150,8 @@
h = PMCLOG_HEADER_FROM_SAVED_STATE(ps);
recordsize = PMCLOG_HEADER_TO_LENGTH(h);
- if (recordsize <= 0)
+ if (recordsize < (int)sizeof(struct pmclog_header) ||
+ recordsize > (int)sizeof(ps->ps_saved))
goto error;
if (recordsize <= avail) { /* full record available */
@@ -213,7 +214,8 @@
h = PMCLOG_HEADER_FROM_SAVED_STATE(ps);
recordsize = PMCLOG_HEADER_TO_LENGTH(h);
- if (recordsize <= 0)
+ if (recordsize < (int)sizeof(struct pmclog_header) ||
+ recordsize > (int)sizeof(ps->ps_saved))
goto error;
if (avail + ps->ps_svcount < recordsize) {
@@ -300,8 +302,13 @@
} while (0)
#define PMCLOG_GET_CALLCHAIN_SIZE(SZ,E) do { \
- (SZ) = ((E) - offsetof(struct pmclog_callchain, pl_pc)) \
- / sizeof(uintfptr_t); \
+ int _payload; \
+ _payload = (E) - offsetof(struct pmclog_callchain, pl_pc); \
+ if (_payload < 0 || _payload % sizeof(uintfptr_t) != 0) \
+ goto error; \
+ (SZ) = _payload / sizeof(uintfptr_t); \
+ if ((SZ) > PMC_CALLCHAIN_DEPTH_MAX) \
+ goto error; \
} while (0);
switch (ev->pl_type = PMCLOG_HEADER_TO_TYPE(h)) {
diff --git a/lib/libpmcstat/libpmcstat_logging.c b/lib/libpmcstat/libpmcstat_logging.c
--- a/lib/libpmcstat/libpmcstat_logging.c
+++ b/lib/libpmcstat/libpmcstat_logging.c
@@ -191,7 +191,7 @@
int *pmcstat_npmcs,
int *ps_samples_period)
{
- uint32_t cpu, cpuflags;
+ uint32_t cpu, cpuflags, noff;
pid_t pid;
struct pmcstat_image *image;
struct pmcstat_process *pp, *ppnew;
@@ -283,6 +283,19 @@
break;
}
+ /* Skip the header and payload of a multipart sample. */
+ noff = 0;
+ if ((cpuflags & PMC_CC_F_MULTIPART) != 0 &&
+ !pmclog_multipart_callchain_offset(ev.pl_u.pl_cc.pl_pc,
+ ev.pl_u.pl_cc.pl_npc, &noff)) {
+ pmcstat_stats->ps_samples_skipped++;
+ break;
+ }
+ if (noff >= ev.pl_u.pl_cc.pl_npc) {
+ pmcstat_stats->ps_samples_skipped++;
+ break;
+ }
+
pp = pmcstat_process_lookup(ev.pl_u.pl_cc.pl_pid,
PMCSTAT_ALLOCATE);
@@ -298,14 +311,14 @@
if (plugins[args->pa_pplugin].pl_process != NULL)
plugins[args->pa_pplugin].pl_process(
pp, pmcr,
- ev.pl_u.pl_cc.pl_npc,
- ev.pl_u.pl_cc.pl_pc,
+ ev.pl_u.pl_cc.pl_npc - noff,
+ ev.pl_u.pl_cc.pl_pc + noff,
PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(cpuflags),
cpu);
plugins[args->pa_plugin].pl_process(
pp, pmcr,
- ev.pl_u.pl_cc.pl_npc,
- ev.pl_u.pl_cc.pl_pc,
+ ev.pl_u.pl_cc.pl_npc - noff,
+ ev.pl_u.pl_cc.pl_pc + noff,
PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(cpuflags),
cpu);
break;
diff --git a/tests/sys/pmc/Makefile b/tests/sys/pmc/Makefile
--- a/tests/sys/pmc/Makefile
+++ b/tests/sys/pmc/Makefile
@@ -9,6 +9,7 @@
.endif
ATF_TESTS_C+= pmc_lifecycle_test
ATF_TESTS_C+= pmc_log_test
+ATF_TESTS_C+= pmc_multipart_test
ATF_TESTS_C+= pmc_wrap_test
TEST_METADATA.pmc_exterr_test+= required_user="root"
diff --git a/tests/sys/pmc/pmc_multipart_test.c b/tests/sys/pmc/pmc_multipart_test.c
new file mode 100644
--- /dev/null
+++ b/tests/sys/pmc/pmc_multipart_test.c
@@ -0,0 +1,315 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 The FreeBSD Foundation
+ *
+ * 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 AUTHOR 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 AUTHOR 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.
+ */
+
+#include <sys/param.h>
+#include <sys/pmc.h>
+#include <sys/pmclog.h>
+
+#include <atf-c.h>
+#include <pmclog.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define PMCLOG_TO_HEADER(T, L) \
+ ((PMCLOG_HEADER_MAGIC << 24) | ((T) << 16) | ((L) & 0xffff))
+
+static size_t
+callchain_record_size(uint32_t npc)
+{
+ return (offsetof(struct pmclog_callchain, pl_pc) +
+ npc * sizeof(uintfptr_t));
+}
+
+static void
+init_callchain_record(struct pmclog_callchain *rec, uint32_t npc,
+ uint32_t cpuflags)
+{
+ size_t len;
+
+ len = callchain_record_size(npc);
+ memset(rec, 0, len);
+ rec->pl_header = PMCLOG_TO_HEADER(PMCLOG_TYPE_CALLCHAIN, len);
+ rec->pl_tsc = 0x12345678;
+ rec->pl_pid = 42;
+ rec->pl_tid = 43;
+ rec->pl_pmcid = 44;
+ rec->pl_cpuflags = cpuflags;
+}
+
+static void
+parse_record(const struct pmclog_callchain *rec, uint32_t npc,
+ struct pmclog_ev *ev)
+{
+ void *parser;
+ size_t len;
+
+ parser = pmclog_open(PMCLOG_FD_NONE);
+ ATF_REQUIRE(parser != NULL);
+ len = callchain_record_size(npc);
+ ATF_REQUIRE_EQ(0, pmclog_feed(parser, (char *)(uintptr_t)rec, len));
+ ATF_REQUIRE_EQ(0, pmclog_read(parser, ev));
+ ATF_REQUIRE_EQ(PMCLOG_OK, ev->pl_state);
+ ATF_REQUIRE_EQ(PMCLOG_TYPE_CALLCHAIN, ev->pl_type);
+ pmclog_close(parser);
+}
+
+ATF_TC(multipart_word_arithmetic);
+ATF_TC_HEAD(multipart_word_arithmetic, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "multipart headers and 64-bit payloads are counted in native "
+ "pointer-sized callchain slots");
+}
+ATF_TC_BODY(multipart_word_arithmetic, tc)
+{
+ ATF_CHECK_EQ(2, PMC_MULTIPART_HEADER_WORDS_FOR(4));
+ ATF_CHECK_EQ(1, PMC_MULTIPART_HEADER_WORDS_FOR(8));
+ ATF_CHECK_EQ(2, PMC_MULTIPART_64BIT_WORDS_FOR(1, 4));
+ ATF_CHECK_EQ(1, PMC_MULTIPART_64BIT_WORDS_FOR(1, 8));
+ ATF_CHECK_EQ(23, PMC_MULTIPART_SAMPLE_MIN_WORDS_FOR(4));
+ ATF_CHECK_EQ(12, PMC_MULTIPART_SAMPLE_MIN_WORDS_FOR(8));
+ ATF_CHECK_EQ(PMC_MULTIPART_HEADER_WORDS_FOR(sizeof(uintfptr_t)),
+ PMC_MULTIPART_HEADER_WORDS);
+}
+
+ATF_TC(multipart_offset_bounds);
+ATF_TC_HEAD(multipart_offset_bounds, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "multipart offset helper handles full payloads, empty records, and "
+ "truncated payloads");
+}
+ATF_TC_BODY(multipart_offset_bounds, tc)
+{
+ uint32_t off, payload_words;
+ uint8_t *hdr;
+ uintfptr_t pc[PMC_MULTIPART_SAMPLE_MIN_WORDS + 1];
+
+ memset(pc, 0, sizeof(pc));
+ hdr = (uint8_t *)pc;
+ payload_words = PMC_MULTIPART_PAYLOAD_WORDS(PMC_MULTIPART_MAX_PAYLOAD64);
+ hdr[0] = PMC_CC_MULTIPART_IBS_OP;
+ hdr[1] = payload_words;
+ hdr[2] = PMC_CC_MULTIPART_CALLCHAIN;
+ hdr[3] = 0;
+ ATF_REQUIRE(pmclog_multipart_callchain_offset(pc,
+ PMC_MULTIPART_SAMPLE_MIN_WORDS, &off));
+ ATF_CHECK_EQ(PMC_MULTIPART_SAMPLE_MIN_WORDS - 1, off);
+
+ ATF_CHECK(!pmclog_multipart_callchain_offset(pc, 0, &off));
+ ATF_CHECK(!pmclog_multipart_callchain_offset(pc,
+ PMC_MULTIPART_HEADER_WORDS - 1, &off));
+ ATF_CHECK(!pmclog_multipart_callchain_offset(pc,
+ PMC_MULTIPART_SAMPLE_MIN_WORDS - 2, &off));
+}
+
+ATF_TC(pmclog_callchain_single_pc);
+ATF_TC_HEAD(pmclog_callchain_single_pc, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "a callchain record without PMC_F_CALLCHAIN still carries its "
+ "single interrupt PC");
+}
+ATF_TC_BODY(pmclog_callchain_single_pc, tc)
+{
+ struct pmclog_callchain rec;
+ struct pmclog_ev ev;
+
+ init_callchain_record(&rec, 1, PMC_CALLCHAIN_TO_CPUFLAGS(3,
+ PMC_CC_F_USERSPACE));
+ rec.pl_pc[0] = (uintfptr_t)0x1234abcd;
+ parse_record(&rec, 1, &ev);
+ ATF_CHECK_EQ(1, ev.pl_u.pl_cc.pl_npc);
+ ATF_CHECK_EQ(3, PMC_CALLCHAIN_CPUFLAGS_TO_CPU(ev.pl_u.pl_cc.pl_cpuflags));
+ ATF_CHECK(PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(ev.pl_u.pl_cc.pl_cpuflags));
+ ATF_CHECK_EQ((uintfptr_t)0x1234abcd, ev.pl_u.pl_cc.pl_pc[0]);
+}
+
+ATF_TC(pmclog_callchain_kernel_and_full_depth);
+ATF_TC_HEAD(pmclog_callchain_kernel_and_full_depth, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "the pmclog parser accepts kernel and full-depth callchain records");
+}
+ATF_TC_BODY(pmclog_callchain_kernel_and_full_depth, tc)
+{
+ struct pmclog_callchain *rec;
+ struct pmclog_ev ev;
+ uint32_t i;
+
+ rec = calloc(1, sizeof(*rec));
+ ATF_REQUIRE(rec != NULL);
+ init_callchain_record(rec, PMC_CALLCHAIN_DEPTH_MAX,
+ PMC_CALLCHAIN_TO_CPUFLAGS(1, 0));
+ for (i = 0; i < PMC_CALLCHAIN_DEPTH_MAX; i++)
+ rec->pl_pc[i] = (uintfptr_t)(0x1000 + i);
+ parse_record(rec, PMC_CALLCHAIN_DEPTH_MAX, &ev);
+ ATF_CHECK_EQ(PMC_CALLCHAIN_DEPTH_MAX, ev.pl_u.pl_cc.pl_npc);
+ ATF_CHECK_EQ(1, PMC_CALLCHAIN_CPUFLAGS_TO_CPU(ev.pl_u.pl_cc.pl_cpuflags));
+ ATF_CHECK(!PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(ev.pl_u.pl_cc.pl_cpuflags));
+ ATF_CHECK_EQ((uintfptr_t)(0x1000 + PMC_CALLCHAIN_DEPTH_MAX - 1),
+ ev.pl_u.pl_cc.pl_pc[PMC_CALLCHAIN_DEPTH_MAX - 1]);
+ free(rec);
+}
+
+ATF_TC(pmclog_callchain_zero_pcs);
+ATF_TC_HEAD(pmclog_callchain_zero_pcs, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "the pmclog parser accepts an empty callchain record without "
+ "reading stale PCs");
+}
+ATF_TC_BODY(pmclog_callchain_zero_pcs, tc)
+{
+ struct pmclog_callchain rec;
+ struct pmclog_ev ev;
+
+ init_callchain_record(&rec, 0, PMC_CALLCHAIN_TO_CPUFLAGS(7, 0));
+ parse_record(&rec, 0, &ev);
+ ATF_CHECK_EQ(0, ev.pl_u.pl_cc.pl_npc);
+ ATF_CHECK_EQ(7, PMC_CALLCHAIN_CPUFLAGS_TO_CPU(ev.pl_u.pl_cc.pl_cpuflags));
+ ATF_CHECK_EQ((uintfptr_t)0, ev.pl_u.pl_cc.pl_pc[0]);
+}
+
+ATF_TC(pmclog_callchain_multipart_minimum_capacity);
+ATF_TC_HEAD(pmclog_callchain_multipart_minimum_capacity, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "a full multipart payload at the minimum sample depth leaves room "
+ "for one callchain PC");
+}
+ATF_TC_BODY(pmclog_callchain_multipart_minimum_capacity, tc)
+{
+ struct pmclog_callchain rec;
+ struct pmclog_ev ev;
+ uint32_t off, payload_words;
+ uint8_t *hdr;
+
+ init_callchain_record(&rec, PMC_MULTIPART_SAMPLE_MIN_WORDS,
+ PMC_CALLCHAIN_TO_CPUFLAGS(0, PMC_CC_F_MULTIPART));
+ hdr = (uint8_t *)rec.pl_pc;
+ payload_words = PMC_MULTIPART_PAYLOAD_WORDS(PMC_MULTIPART_MAX_PAYLOAD64);
+ hdr[0] = PMC_CC_MULTIPART_IBS_FETCH;
+ hdr[1] = payload_words;
+ hdr[2] = PMC_CC_MULTIPART_CALLCHAIN;
+ rec.pl_pc[PMC_MULTIPART_SAMPLE_MIN_WORDS - 1] = (uintfptr_t)0xfeedface;
+
+ parse_record(&rec, PMC_MULTIPART_SAMPLE_MIN_WORDS, &ev);
+ ATF_REQUIRE(pmclog_multipart_callchain_offset(ev.pl_u.pl_cc.pl_pc,
+ ev.pl_u.pl_cc.pl_npc, &off));
+ ATF_CHECK_EQ(PMC_MULTIPART_SAMPLE_MIN_WORDS - 1, off);
+ ATF_CHECK_EQ((uintfptr_t)0xfeedface, ev.pl_u.pl_cc.pl_pc[off]);
+}
+
+ATF_TC(pmclog_callchain_fragmented_record);
+ATF_TC_HEAD(pmclog_callchain_fragmented_record, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "the pmclog parser waits for the remainder of a truncated record");
+}
+ATF_TC_BODY(pmclog_callchain_fragmented_record, tc)
+{
+ struct pmclog_callchain rec;
+ struct pmclog_ev ev;
+ void *parser;
+ size_t len;
+
+ init_callchain_record(&rec, 1, PMC_CALLCHAIN_TO_CPUFLAGS(2,
+ PMC_CC_F_USERSPACE));
+ rec.pl_pc[0] = (uintfptr_t)0xabcdef;
+ len = callchain_record_size(1);
+ parser = pmclog_open(PMCLOG_FD_NONE);
+ ATF_REQUIRE(parser != NULL);
+ ATF_REQUIRE_EQ(0, pmclog_feed(parser, (char *)&rec, len - 1));
+ ATF_CHECK_EQ(-1, pmclog_read(parser, &ev));
+ ATF_CHECK_EQ(PMCLOG_REQUIRE_DATA, ev.pl_state);
+ ATF_REQUIRE_EQ(0, pmclog_feed(parser, ((char *)&rec) + len - 1, 1));
+ ATF_REQUIRE_EQ(0, pmclog_read(parser, &ev));
+ ATF_REQUIRE_EQ(PMCLOG_OK, ev.pl_state);
+ ATF_CHECK_EQ(1, ev.pl_u.pl_cc.pl_npc);
+ ATF_CHECK_EQ((uintfptr_t)0xabcdef, ev.pl_u.pl_cc.pl_pc[0]);
+ pmclog_close(parser);
+}
+
+ATF_TC(pmclog_rejects_malformed_lengths);
+ATF_TC_HEAD(pmclog_rejects_malformed_lengths, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "pmclog rejects zero, oversized, and unaligned record lengths");
+}
+ATF_TC_BODY(pmclog_rejects_malformed_lengths, tc)
+{
+ struct pmclog_callchain rec;
+ struct pmclog_ev ev;
+ void *parser;
+ size_t badlen;
+
+ memset(&rec, 0, sizeof(rec));
+ rec.pl_header = PMCLOG_TO_HEADER(PMCLOG_TYPE_CALLCHAIN, 0);
+ parser = pmclog_open(PMCLOG_FD_NONE);
+ ATF_REQUIRE(parser != NULL);
+ ATF_REQUIRE_EQ(0, pmclog_feed(parser, (char *)&rec, sizeof(uint32_t)));
+ ATF_CHECK_EQ(-1, pmclog_read(parser, &ev));
+ ATF_CHECK_EQ(PMCLOG_ERROR, ev.pl_state);
+ pmclog_close(parser);
+
+ memset(&rec, 0, sizeof(rec));
+ rec.pl_header = PMCLOG_TO_HEADER(PMCLOG_TYPE_CALLCHAIN,
+ sizeof(union pmclog_entry) + sizeof(uint32_t));
+ parser = pmclog_open(PMCLOG_FD_NONE);
+ ATF_REQUIRE(parser != NULL);
+ ATF_REQUIRE_EQ(0, pmclog_feed(parser, (char *)&rec, sizeof(uint32_t)));
+ ATF_CHECK_EQ(-1, pmclog_read(parser, &ev));
+ ATF_CHECK_EQ(PMCLOG_ERROR, ev.pl_state);
+ pmclog_close(parser);
+
+ badlen = offsetof(struct pmclog_callchain, pl_pc) + 1;
+ memset(&rec, 0, sizeof(rec));
+ rec.pl_header = PMCLOG_TO_HEADER(PMCLOG_TYPE_CALLCHAIN, badlen);
+ parser = pmclog_open(PMCLOG_FD_NONE);
+ ATF_REQUIRE(parser != NULL);
+ ATF_REQUIRE_EQ(0, pmclog_feed(parser, (char *)&rec, badlen));
+ ATF_CHECK_EQ(-1, pmclog_read(parser, &ev));
+ ATF_CHECK_EQ(PMCLOG_ERROR, ev.pl_state);
+ pmclog_close(parser);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, multipart_word_arithmetic);
+ ATF_TP_ADD_TC(tp, multipart_offset_bounds);
+ ATF_TP_ADD_TC(tp, pmclog_callchain_single_pc);
+ ATF_TP_ADD_TC(tp, pmclog_callchain_kernel_and_full_depth);
+ ATF_TP_ADD_TC(tp, pmclog_callchain_zero_pcs);
+ ATF_TP_ADD_TC(tp, pmclog_callchain_multipart_minimum_capacity);
+ ATF_TP_ADD_TC(tp, pmclog_callchain_fragmented_record);
+ ATF_TP_ADD_TC(tp, pmclog_rejects_malformed_lengths);
+
+ return (atf_no_error());
+}
diff --git a/usr.sbin/pmcstat/pmcstat_log.c b/usr.sbin/pmcstat/pmcstat_log.c
--- a/usr.sbin/pmcstat/pmcstat_log.c
+++ b/usr.sbin/pmcstat/pmcstat_log.c
@@ -370,12 +370,14 @@
}
#if defined(__amd64__) || defined(__i386__)
-static void
+static int
pmcstat_print_ibs_fetch(struct pmclog_ev_callchain *cc, int offset, int len64)
{
uint64_t *ibsbuf = (uint64_t *)&cc->pl_pc[offset];
uint64_t ctl, ctl2;
+ if (len64 <= PMC_MPIDX_FETCH_LINADDR)
+ return (0);
ctl = ibsbuf[PMC_MPIDX_FETCH_CTL];
PMCSTAT_PRINT_ENTRY("ibs-fetch", "%s%s%s%s",
(ctl & IBS_FETCH_CTL_ICMISS) ? "icmiss " : "",
@@ -387,6 +389,8 @@
PMCSTAT_PRINT_ENTRY("IBS", "Address %" PRIx64,
ibsbuf[PMC_MPIDX_FETCH_LINADDR]);
if ((ctl & IBS_FETCH_CTL_PHYSADDRVALID) != 0) {
+ if (len64 <= PMC_MPIDX_FETCH_PHYSADDR)
+ return (0);
PMCSTAT_PRINT_ENTRY("IBS", "Physical Address %" PRIx64,
ibsbuf[PMC_MPIDX_FETCH_PHYSADDR]);
}
@@ -402,19 +406,24 @@
(uint64_t)IBS_FETCH_CTL2_CTL_TO_LAT(ctl2));
}
}
+ return (1);
}
-static void
+static int
pmcstat_print_ibs_op(struct pmclog_ev_callchain *cc, int offset, int len64)
{
uint64_t *ibsbuf = (uint64_t *)&cc->pl_pc[offset];
uint64_t data, data2, data3, ctl2;
+ if (len64 <= PMC_MPIDX_OP_DATA3)
+ return (0);
data = ibsbuf[PMC_MPIDX_OP_DATA];
data2 = ibsbuf[PMC_MPIDX_OP_DATA2];
data3 = ibsbuf[PMC_MPIDX_OP_DATA3];
if ((data & IBS_OP_DATA_RIPINVALID) == 0) {
+ if (len64 <= PMC_MPIDX_OP_RIP)
+ return (0);
PMCSTAT_PRINT_ENTRY("ibs-op", "RIP %" PRIx64,
ibsbuf[PMC_MPIDX_OP_RIP]);
}
@@ -437,10 +446,14 @@
PMCSTAT_PRINT_ENTRY("ibs-op", "Latency %" PRIu64,
IBS_OP_DATA3_TO_DCLAT(data3));
if ((data3 & IBS_OP_DATA3_DCLINADDRVALID) != 0) {
+ if (len64 <= PMC_MPIDX_OP_DC_LINADDR)
+ return (0);
PMCSTAT_PRINT_ENTRY("ibs-op", "Address %" PRIx64,
ibsbuf[PMC_MPIDX_OP_DC_LINADDR]);
}
if ((data3 & IBS_OP_DATA3_DCPHYADDRVALID) != 0) {
+ if (len64 <= PMC_MPIDX_OP_DC_PHYSADDR)
+ return (0);
PMCSTAT_PRINT_ENTRY("ibs-op", "Physical Address %" PRIx64,
ibsbuf[PMC_MPIDX_OP_DC_PHYSADDR]);
}
@@ -453,6 +466,7 @@
if ((ctl2 & IBS_OP_CTL2_STRMSTFILTER) != 0)
PMCSTAT_PRINT_ENTRY("ibs-op", "streamstore");
}
+ return (1);
}
#endif
@@ -461,8 +475,13 @@
{
int i;
uint8_t *hdr = (uint8_t *)&cc->pl_pc[0];
- int offset = PMC_MULTIPART_HEADER_LENGTH / sizeof(uintptr_t);
+ uint32_t offset = PMC_MULTIPART_HEADER_WORDS;
+ uint32_t words_per_64 = sizeof(uint64_t) / sizeof(uintptr_t);
+ if (cc->pl_npc < offset) {
+ PMCSTAT_PRINT_ENTRY("truncated multipart record!");
+ return (cc->pl_npc);
+ }
for (i = 0; i < PMC_MULTIPART_HEADER_ENTRIES; i++) {
uint8_t type = hdr[2 * i];
uint8_t len = hdr[2 * i + 1];
@@ -471,13 +490,23 @@
break;
} else if (type == PMC_CC_MULTIPART_CALLCHAIN) {
return (offset);
+ } else if (len > cc->pl_npc - offset) {
+ PMCSTAT_PRINT_ENTRY("truncated multipart record!");
+ return (cc->pl_npc);
#if defined(__amd64__) || defined(__i386__)
} else if (type == PMC_CC_MULTIPART_IBS_FETCH) {
- pmcstat_print_ibs_fetch(cc, offset,
- len / (sizeof(uint64_t) / sizeof(uintptr_t)));
+ if (len % words_per_64 != 0 ||
+ !pmcstat_print_ibs_fetch(cc, offset,
+ len / words_per_64)) {
+ PMCSTAT_PRINT_ENTRY("truncated multipart record!");
+ return (cc->pl_npc);
+ }
} else if (type == PMC_CC_MULTIPART_IBS_OP) {
- pmcstat_print_ibs_op(cc, offset,
- len / (sizeof(uint64_t) / sizeof(uintptr_t)));
+ if (len % words_per_64 != 0 ||
+ !pmcstat_print_ibs_op(cc, offset, len / words_per_64)) {
+ PMCSTAT_PRINT_ENTRY("truncated multipart record!");
+ return (cc->pl_npc);
+ }
#endif
} else {
PMCSTAT_PRINT_ENTRY("unsupported multipart type!");
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Oct 12, 12:53 AM (2 h, 41 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
40585653
Default Alt Text
D60564.diff (19 KB)
Attached To
Mode
D60564: libpmc, pmcstat: harden multipart sample consumers
Attached
Detach File
Event Timeline
Log In to Comment