Implement PERF_EVENT_IOC_* decoding

* perf.c (fetch_perf_event_attr, print_perf_event_attr): Remove "static"
qualifier.
* defs.h (fetch_perf_event_attr, print_perf_event_attr): New
declarations.
* perf_event_struct.h (struct perf_event_query_bpf): New type
definition.
* perf_ioctl.c: New file.
* Makefile.am (strace_SOURCES): Add it.
* xlat/perf_ioctl_cmds.in: New file.
* xlat/perf_ioctl_flags.in: Likewise.
* ioctl.c (ioctl_decode) <case '$'>: Call perf_ioctl.
* tests/gen_tests.in (ioctl_perf): New test.
* tests/ioctl_perf.c: New file.
* tests/pure_executables.list: ioctl_perf.
* tests/.gitignore: Likewise.

Co-Authored-by: Dmitry V. Levin <ldv@altlinux.org>
This commit is contained in:
Eugene Syromyatnikov 2018-05-15 18:45:28 +02:00 committed by Dmitry V. Levin
parent d5c63293c1
commit 7adb5a2f9e
12 changed files with 417 additions and 2 deletions

View File

@ -235,6 +235,7 @@ strace_SOURCES = \
pathtrace.c \
perf.c \
perf_event_struct.h \
perf_ioctl.c \
personality.c \
pkeys.c \
poll.c \

5
defs.h
View File

@ -845,6 +845,11 @@ print_struct_statfs(struct tcb *, kernel_ulong_t addr);
extern void
print_struct_statfs64(struct tcb *, kernel_ulong_t addr, kernel_ulong_t size);
extern int
fetch_perf_event_attr(struct tcb *const tcp, const kernel_ulong_t addr);
extern void
print_perf_event_attr(struct tcb *const tcp, const kernel_ulong_t addr);
extern void print_ifindex(unsigned int);
extern void print_bpf_filter_code(const uint16_t code, bool extended);

View File

@ -255,6 +255,8 @@ ioctl_decode(struct tcb *tcp)
const kernel_ulong_t arg = tcp->u_arg[2];
switch (_IOC_TYPE(code)) {
case '$':
return perf_ioctl(tcp, code, arg);
#if defined(ALPHA) || defined(POWERPC)
case 'f': {
int ret = file_ioctl(tcp, code, arg);

4
perf.c
View File

@ -60,7 +60,7 @@ free_pea_desc(void *pea_desc_ptr)
free(desc);
}
static int
int
fetch_perf_event_attr(struct tcb *const tcp, const kernel_ulong_t addr)
{
struct pea_desc *desc;
@ -117,7 +117,7 @@ fetch_perf_event_attr(struct tcb *const tcp, const kernel_ulong_t addr)
printxval_search(xlat, x, dflt); \
} while (0)
static void
void
print_perf_event_attr(struct tcb *const tcp, const kernel_ulong_t addr)
{
static const char *precise_ip_desc[] = {

View File

@ -71,4 +71,10 @@ struct perf_event_attr {
/* End of ver 5 - 112 bytes */
};
struct perf_event_query_bpf {
uint32_t ids_len;
uint32_t prog_cnt;
uint32_t ids[0];
};
#endif /* !STRACE_LINUX_PERF_EVENT_STRUCT_H */

153
perf_ioctl.c Normal file
View File

@ -0,0 +1,153 @@
/*
* Copyright (c) 2018 The strace developers.
* All rights reserved.
*
* 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.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 "defs.h"
#include <linux/ioctl.h>
#include "perf_event_struct.h"
#define XLAT_MACROS_ONLY
# include "xlat/perf_ioctl_cmds.h"
#undef XLAT_MACROS_ONLY
#include "xlat/perf_ioctl_flags.h"
#include MPERS_DEFS
static int
perf_ioctl_query_bpf(struct tcb *const tcp, const kernel_ulong_t arg)
{
uint32_t info;
if (entering(tcp)) {
tprints(", ");
if (umove_or_printaddr(tcp, arg, &info))
return RVAL_IOCTL_DECODED;
tprintf("{ids_len=%u, ", info);
return 0;
}
if (syserror(tcp) ||
umove(tcp, arg + offsetof(struct perf_event_query_bpf, prog_cnt),
&info)) {
tprints("...}");
return RVAL_IOCTL_DECODED;
}
tprintf("prog_cnt=%u, ids=", info);
print_array(tcp, arg + offsetof(struct perf_event_query_bpf, ids), info,
&info, sizeof(info),
umoven_or_printaddr, print_uint32_array_member, NULL);
tprints("}");
return RVAL_IOCTL_DECODED;
}
static int
perf_ioctl_modify_attributes(struct tcb *const tcp, const kernel_ulong_t arg)
{
tprints(", ");
if (!fetch_perf_event_attr(tcp, arg))
print_perf_event_attr(tcp, arg);
return RVAL_IOCTL_DECODED;
}
MPERS_PRINTER_DECL(int, perf_ioctl,
struct tcb *const tcp, const unsigned int code,
const kernel_ulong_t arg)
{
switch (code) {
case PERF_EVENT_IOC_ENABLE:
case PERF_EVENT_IOC_DISABLE:
case PERF_EVENT_IOC_RESET:
tprints(", ");
printflags(perf_ioctl_flags, arg, "PERF_IOC_FLAG_???");
return RVAL_IOCTL_DECODED;
case PERF_EVENT_IOC_REFRESH:
tprintf(", %d", (int) arg);
return RVAL_IOCTL_DECODED;
case PERF_EVENT_IOC_PERIOD:
tprints(", ");
printnum_int64(tcp, arg, "%" PRIu64);
return RVAL_IOCTL_DECODED;
case PERF_EVENT_IOC_SET_OUTPUT:
case PERF_EVENT_IOC_SET_BPF:
tprintf(", ");
printfd(tcp, (int) arg);
return RVAL_IOCTL_DECODED;
case PERF_EVENT_IOC_PAUSE_OUTPUT:
tprintf(", %" PRI_klu, arg);
return RVAL_IOCTL_DECODED;
/*
* The following ioctl requests are personality-specific
* due to the pointer size.
*/
case PERF_EVENT_IOC_SET_FILTER:
tprints(", ");
printstr_ex(tcp, arg, get_pagesize(), QUOTE_0_TERMINATED);
return RVAL_IOCTL_DECODED;
case PERF_EVENT_IOC_ID:
if (entering(tcp)) {
tprints(", ");
return 0;
}
printnum_int64(tcp, arg, "%" PRIu64);
return RVAL_IOCTL_DECODED;
case PERF_EVENT_IOC_QUERY_BPF:
return perf_ioctl_query_bpf(tcp, arg);
case PERF_EVENT_IOC_MODIFY_ATTRIBUTES:
return perf_ioctl_modify_attributes(tcp, arg);
default:
return RVAL_DECODED;
}
}

1
tests/.gitignore vendored
View File

@ -143,6 +143,7 @@ ioctl_loop-nv
ioctl_loop-v
ioctl_mtd
ioctl_nsfs
ioctl_perf
ioctl_ptp
ioctl_rtc
ioctl_rtc-v

View File

@ -140,6 +140,7 @@ ioctl_loop-nv +ioctl.test -a22 -e verbose=none
ioctl_loop-v +ioctl.test -v
ioctl_mtd +ioctl.test
ioctl_nsfs +ioctl.test -esignal=none
ioctl_perf +ioctl.test
ioctl_ptp +ioctl.test
ioctl_rtc +ioctl.test
ioctl_rtc-v +ioctl.test -v

232
tests/ioctl_perf.c Normal file
View File

@ -0,0 +1,232 @@
/*
* Check decoding of PERF_EVENT_IOC_* commands of ioctl syscall.
*
* Copyright (c) 2018 The strace developers.
* All rights reserved.
*
* 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.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 "tests.h"
#ifdef HAVE_LINUX_PERF_EVENT_H
# include <inttypes.h>
# include <stdio.h>
# include <string.h>
# include <sys/ioctl.h>
# include <linux/perf_event.h>
/*
* Workaround the bug in kernel UAPI that was fixed
* in Linux commit v2.6.33-rc1~48^2~288^2~19.
*/
# ifndef u64
# define u64 uint64_t
# endif
# define XLAT_MACROS_ONLY
# include "xlat/perf_ioctl_cmds.h"
# undef XLAT_MACROS_ONLY
# define STR16 "0123456789abcdef"
int
main(void)
{
static const kernel_ulong_t unknown_perf_cmd =
(kernel_ulong_t) 0xbadc0dedfeed24edULL;
static const kernel_ulong_t magic =
(kernel_ulong_t) 0xdeadbeefbadc0dedULL;
static const uint64_t magic64 = 0xfacefeeddeadc0deULL;
static const char str[] = STR16 STR16 STR16 STR16;
static struct {
unsigned int cmd;
const char *str;
} flag_iocs[] = {
{ ARG_STR(PERF_EVENT_IOC_ENABLE) },
{ ARG_STR(PERF_EVENT_IOC_DISABLE) },
{ ARG_STR(PERF_EVENT_IOC_RESET) },
};
TAIL_ALLOC_OBJECT_CONST_PTR(uint64_t, u64_ptr);
uint64_t *const u64_efault = u64_ptr + 1;
uint32_t *const u32_arr = tail_alloc(sizeof(uint32_t) * 4);
uint32_t *const u32_efault = u32_arr + 4;
char *const str_ptr = tail_memdup(str, sizeof(str));
char *const str_efault = str_ptr + sizeof(str);
TAIL_ALLOC_OBJECT_CONST_PTR(struct perf_event_attr, pea_ptr);
*u64_ptr = magic64;
fill_memory_ex(pea_ptr, sizeof(*pea_ptr), 0xaa, 0x55);
/* Unknown perf commands */
ioctl(-1, unknown_perf_cmd, magic);
printf("ioctl(-1, _IOC(_IOC_READ|_IOC_WRITE%s, 0x24, %#x, %#x), "
"%#lx) = -1 EBADF (%m)\n",
_IOC_DIR((unsigned int) unknown_perf_cmd) & _IOC_NONE ?
"|_IOC_NONE" : "",
_IOC_NR((unsigned int) unknown_perf_cmd),
_IOC_SIZE((unsigned int) unknown_perf_cmd),
(unsigned long) magic);
ioctl(-1, PERF_EVENT_IOC_MODIFY_ATTRIBUTES + 1, magic);
printf("ioctl(-1, _IOC(_IOC_WRITE, 0x24, %#x, %#x), %#lx)"
" = -1 EBADF (%m)\n",
(unsigned int) _IOC_NR(PERF_EVENT_IOC_MODIFY_ATTRIBUTES + 1),
(unsigned int) _IOC_SIZE(PERF_EVENT_IOC_MODIFY_ATTRIBUTES + 1),
(unsigned long) magic);
/* PERF_EVENT_IOC_{ENABLE,DISABLE,RESET} */
for (unsigned i = 0; i < ARRAY_SIZE(flag_iocs); i++) {
ioctl(-1, flag_iocs[i].cmd, 0);
printf("ioctl(-1, %s, 0) = -1 EBADF (%m)\n", flag_iocs[i].str);
ioctl(-1, flag_iocs[i].cmd, 1);
printf("ioctl(-1, %s, PERF_IOC_FLAG_GROUP) = -1 EBADF (%m)\n",
flag_iocs[i].str);
ioctl(-1, flag_iocs[i].cmd, 2);
printf("ioctl(-1, %s, 0x2 /* PERF_IOC_FLAG_??? */) "
"= -1 EBADF (%m)\n",
flag_iocs[i].str);
ioctl(-1, flag_iocs[i].cmd, magic);
printf("ioctl(-1, %s, PERF_IOC_FLAG_GROUP|%#x) "
"= -1 EBADF (%m)\n",
flag_iocs[i].str, (unsigned int) magic & ~1U);
}
/* PERF_EVENT_IOC_REFRESH */
ioctl(-1, PERF_EVENT_IOC_REFRESH, magic);
printf("ioctl(-1, PERF_EVENT_IOC_REFRESH, %d) = -1 EBADF (%m)\n",
(int) magic);
/* PERF_EVENT_IOC_PERIOD */
ioctl(-1, PERF_EVENT_IOC_PERIOD, NULL);
printf("ioctl(-1, PERF_EVENT_IOC_PERIOD, NULL) = -1 EBADF (%m)\n");
ioctl(-1, PERF_EVENT_IOC_PERIOD, u64_efault);
printf("ioctl(-1, PERF_EVENT_IOC_PERIOD, %p) = -1 EBADF (%m)\n",
u64_efault);
ioctl(-1, PERF_EVENT_IOC_PERIOD, u64_ptr);
printf("ioctl(-1, PERF_EVENT_IOC_PERIOD, [%" PRIu64 "])"
" = -1 EBADF (%m)\n",
magic64);
/* PERF_EVENT_IOC_SET_OUTPUT */
ioctl(-1, PERF_EVENT_IOC_SET_OUTPUT, magic);
printf("ioctl(-1, PERF_EVENT_IOC_SET_OUTPUT, %d) = -1 EBADF (%m)\n",
(int) magic);
/* PERF_EVENT_IOC_SET_FILTER */
ioctl(-1, PERF_EVENT_IOC_SET_FILTER, NULL);
printf("ioctl(-1, PERF_EVENT_IOC_SET_FILTER, NULL) = -1 EBADF (%m)\n");
ioctl(-1, PERF_EVENT_IOC_SET_FILTER, str_efault);
printf("ioctl(-1, PERF_EVENT_IOC_SET_FILTER, %p) = -1 EBADF (%m)\n",
str_efault);
ioctl(-1, PERF_EVENT_IOC_SET_FILTER, str_ptr);
printf("ioctl(-1, PERF_EVENT_IOC_SET_FILTER, \"%.32s\"...)"
" = -1 EBADF (%m)\n",
str_ptr);
ioctl(-1, PERF_EVENT_IOC_SET_FILTER, str_ptr + 40);
printf("ioctl(-1, PERF_EVENT_IOC_SET_FILTER, \"%.32s\")"
" = -1 EBADF (%m)\n",
str_ptr + 40);
str_ptr[sizeof(str) - 1] = '0';
ioctl(-1, PERF_EVENT_IOC_SET_FILTER, str_ptr + 40);
printf("ioctl(-1, PERF_EVENT_IOC_SET_FILTER, %p)"
" = -1 EBADF (%m)\n",
str_ptr + 40);
/* PERF_EVENT_IOC_ID */
/* TODO: check return path */
ioctl(-1, PERF_EVENT_IOC_ID, NULL);
printf("ioctl(-1, PERF_EVENT_IOC_ID, NULL) = -1 EBADF (%m)\n");
ioctl(-1, PERF_EVENT_IOC_ID, u64_efault);
printf("ioctl(-1, PERF_EVENT_IOC_ID, %p) = -1 EBADF (%m)\n",
u64_efault);
ioctl(-1, PERF_EVENT_IOC_ID, u64_ptr);
printf("ioctl(-1, PERF_EVENT_IOC_ID, %p) = -1 EBADF (%m)\n",
u64_ptr);
/* PERF_EVENT_IOC_SET_BPF */
ioctl(-1, PERF_EVENT_IOC_SET_BPF, magic);
printf("ioctl(-1, PERF_EVENT_IOC_SET_BPF, %d) = -1 EBADF (%m)\n",
(int) magic);
/* PERF_EVENT_IOC_PAUSE_OUTPUT */
ioctl(-1, PERF_EVENT_IOC_PAUSE_OUTPUT, magic);
printf("ioctl(-1, PERF_EVENT_IOC_PAUSE_OUTPUT, %lu) = -1 EBADF (%m)\n",
(unsigned long) magic);
/* PERF_EVENT_IOC_QUERY_BPF */
/* TODO: check return path */
ioctl(-1, PERF_EVENT_IOC_QUERY_BPF, NULL);
printf("ioctl(-1, PERF_EVENT_IOC_QUERY_BPF, NULL) = -1 EBADF (%m)\n");
ioctl(-1, PERF_EVENT_IOC_QUERY_BPF, u32_efault);
printf("ioctl(-1, PERF_EVENT_IOC_QUERY_BPF, %p) = -1 EBADF (%m)\n",
u32_efault);
u32_arr[0] = 0xbadc0ded;
ioctl(-1, PERF_EVENT_IOC_QUERY_BPF, u32_arr);
printf("ioctl(-1, PERF_EVENT_IOC_QUERY_BPF, {ids_len=3134983661, ...})"
" = -1 EBADF (%m)\n");
/* PERF_EVENT_IOC_MODIFY_ATTRIBUTES */
ioctl(-1, PERF_EVENT_IOC_MODIFY_ATTRIBUTES, NULL);
printf("ioctl(-1, PERF_EVENT_IOC_MODIFY_ATTRIBUTES, NULL)"
" = -1 EBADF (%m)\n");
ioctl(-1, PERF_EVENT_IOC_MODIFY_ATTRIBUTES, pea_ptr + 1);
printf("ioctl(-1, PERF_EVENT_IOC_MODIFY_ATTRIBUTES, %p)"
" = -1 EBADF (%m)\n",
pea_ptr + 1);
printf("ioctl(-1, PERF_EVENT_IOC_MODIFY_ATTRIBUTES"
", {type=%#x /* PERF_TYPE_??? */"
", size=%#x /* PERF_ATTR_SIZE_??? */"
", config=%#llx, ...}) = -1 EBADF (%m)\n",
(unsigned int) pea_ptr->type,
(unsigned int) pea_ptr->size,
(unsigned long long) pea_ptr->config);
ioctl(-1, PERF_EVENT_IOC_MODIFY_ATTRIBUTES, pea_ptr);
puts("+++ exited with 0 +++");
return 0;
}
#else
SKIP_MAIN_UNDEFINED("HAVE_LINUX_PERF_EVENT_H");
#endif

View File

@ -112,6 +112,7 @@ ioctl_kvm_run
ioctl_loop
ioctl_mtd
ioctl_rtc
ioctl_perf
ioctl_ptp
ioctl_scsi
ioctl_sg_io_v3

12
xlat/perf_ioctl_cmds.in Normal file
View File

@ -0,0 +1,12 @@
PERF_EVENT_IOC_ENABLE _IO ('$', 0)
PERF_EVENT_IOC_DISABLE _IO ('$', 1)
PERF_EVENT_IOC_REFRESH _IO ('$', 2)
PERF_EVENT_IOC_RESET _IO ('$', 3)
PERF_EVENT_IOC_PERIOD _IOW('$', 4, uint64_t)
PERF_EVENT_IOC_SET_OUTPUT _IO ('$', 5)
PERF_EVENT_IOC_SET_FILTER _IOW('$', 6, char *)
PERF_EVENT_IOC_ID _IOR('$', 7, uint64_t *)
PERF_EVENT_IOC_SET_BPF _IOW('$', 8, uint32_t)
PERF_EVENT_IOC_PAUSE_OUTPUT _IOW('$', 9, uint32_t)
PERF_EVENT_IOC_QUERY_BPF _IOWR('$', 10, struct perf_event_query_bpf *)
PERF_EVENT_IOC_MODIFY_ATTRIBUTES _IOW('$', 11, struct perf_event_attr *)

1
xlat/perf_ioctl_flags.in Normal file
View File

@ -0,0 +1 @@
PERF_IOC_FLAG_GROUP (1U << 0)