diff options
Diffstat (limited to 'src/ldt.c')
-rw-r--r-- | src/ldt.c | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/src/ldt.c b/src/ldt.c new file mode 100644 index 000000000..de93cf896 --- /dev/null +++ b/src/ldt.c @@ -0,0 +1,202 @@ +/* + * Copyright (c) 1991, 1992 Paul Kranenburg <pk@cs.few.eur.nl> + * Copyright (c) 1993 Branko Lankester <branko@hacktic.nl> + * Copyright (c) 1993-1996 Rick Sladkey <jrs@world.std.com> + * Copyright (c) 1996-1999 Wichert Akkerman <wichert@cistron.nl> + * Copyright (c) 2002-2004 Roland McGrath <roland@redhat.com> + * Copyright (c) 2010 Andreas Schwab <schwab@linux-m68k.org> + * Copyright (c) 2014-2015 Dmitry V. Levin <ldv@strace.io> + * Copyright (c) 2014-2018 The strace developers. + * All rights reserved. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "defs.h" + +#ifdef HAVE_STRUCT_USER_DESC + +# include <asm/ldt.h> + +# include "xstring.h" + +void +print_user_desc(struct tcb *const tcp, const kernel_ulong_t addr, + enum user_desc_print_filter filter) +{ + struct user_desc desc; + unsigned *entry_number = get_tcb_priv_data(tcp); + + switch (filter) { + case USER_DESC_ENTERING: + if (umove_or_printaddr(tcp, addr, &desc.entry_number)) + return; + + break; + + case USER_DESC_EXITING: + if (!addr || !verbose(tcp)) + return; + if (syserror(tcp) || umove(tcp, addr, &desc)) { + if (entry_number) { + tprint_struct_next(); + tprint_more_data_follows(); + tprint_struct_end(); + } + + return; + } + + break; + + case USER_DESC_BOTH: + if (umove_or_printaddr(tcp, addr, &desc)) + return; + + break; + } + + if (filter & USER_DESC_ENTERING) { + tprint_struct_begin(); + PRINT_FIELD_ID(desc, entry_number); + + /* + * If we don't print the whole structure now, let's save it for + * later. + */ + if (filter == USER_DESC_ENTERING) { + entry_number = xmalloc(sizeof(*entry_number)); + + *entry_number = desc.entry_number; + set_tcb_priv_data(tcp, entry_number, free); + } + } + + if (filter & USER_DESC_EXITING) { + /* + * It should be the same in case of get_thread_area, but we can + * never be sure... + */ + if (filter == USER_DESC_EXITING) { + if (entry_number) { + if (*entry_number != desc.entry_number) { + tprint_value_changed(); + if ((int) desc.entry_number == -1) + tprints("-1"); + else + tprintf("%u", + desc.entry_number); + } + } else { + /* + * This is really strange. If we are here, it + * means that we failed on entering but somehow + * succeeded on exiting. + */ + tprint_value_changed(); + tprint_struct_begin(); + PRINT_FIELD_ID(desc, entry_number); + } + } + + tprint_struct_next(); + PRINT_FIELD_0X(desc, base_addr); + tprint_struct_next(); + PRINT_FIELD_0X(desc, limit); + tprint_struct_next(); + PRINT_FIELD_U_CAST(desc, seg_32bit, unsigned int); + tprint_struct_next(); + PRINT_FIELD_U_CAST(desc, contents, unsigned int); + tprint_struct_next(); + PRINT_FIELD_U_CAST(desc, read_exec_only, unsigned int); + tprint_struct_next(); + PRINT_FIELD_U_CAST(desc, limit_in_pages, unsigned int); + tprint_struct_next(); + PRINT_FIELD_U_CAST(desc, seg_not_present, unsigned int); + tprint_struct_next(); + PRINT_FIELD_U_CAST(desc, useable, unsigned int); + +# ifdef HAVE_STRUCT_USER_DESC_LM + /* lm is totally ignored for 32-bit processes */ + if (current_klongsize == 8) { + tprint_struct_next(); + PRINT_FIELD_U_CAST(desc, lm, unsigned int); + } +# endif /* HAVE_STRUCT_USER_DESC_LM */ + + tprint_struct_end(); + } +} + +SYS_FUNC(modify_ldt) +{ + if (entering(tcp)) { + tprintf("%d, ", (int) tcp->u_arg[0]); + if (tcp->u_arg[2] != sizeof(struct user_desc)) + printaddr(tcp->u_arg[1]); + else + print_user_desc(tcp, tcp->u_arg[1], USER_DESC_BOTH); + tprintf(", %" PRI_klu, tcp->u_arg[2]); + + return 0; + } + + /* + * For some reason ("tht ABI for sys_modify_ldt() expects + * 'int'"), modify_ldt clips higher bits on x86_64. + */ + + if (syserror(tcp) || (kernel_ulong_t) tcp->u_rval < 0xfffff000) + return 0; + + tcp->u_error = -(unsigned int) tcp->u_rval; + + return 0; +} + +SYS_FUNC(set_thread_area) +{ + if (entering(tcp)) { + print_user_desc(tcp, tcp->u_arg[0], USER_DESC_BOTH); + } else { + struct user_desc desc; + + if (!verbose(tcp) || syserror(tcp) || + umove(tcp, tcp->u_arg[0], &desc) < 0) { + /* returned entry_number is not available */ + } else { + static char outstr[32]; + + xsprintf(outstr, "entry_number=%u", desc.entry_number); + tcp->auxstr = outstr; + return RVAL_STR; + } + } + return 0; +} + +SYS_FUNC(get_thread_area) +{ + print_user_desc(tcp, tcp->u_arg[0], + entering(tcp) ? USER_DESC_ENTERING : USER_DESC_EXITING); + return 0; +} + +#endif /* HAVE_STRUCT_USER_DESC */ + +#if defined(M68K) || defined(MIPS) +SYS_FUNC(set_thread_area) +{ + printaddr(tcp->u_arg[0]); + + return RVAL_DECODED; + +} +#endif + +#if defined(M68K) +SYS_FUNC(get_thread_area) +{ + return RVAL_DECODED | RVAL_HEX; +} +#endif |