From d3b90d8f86959d20b235d03648d9f70c2b2dfbbb Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Tue, 24 Mar 2026 09:47:47 +0800 Subject: [PATCH 001/110] Revert "gen_init_cpio: attempt copy_file_range for file data" This reverts commit 97169cd6d95b338f2dbf5d3154b7d2233fce7d8a. --- usr/gen_init_cpio.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/usr/gen_init_cpio.c b/usr/gen_init_cpio.c index b7296edc6626..ced75532f08e 100644 --- a/usr/gen_init_cpio.c +++ b/usr/gen_init_cpio.c @@ -367,7 +367,6 @@ static int cpio_mkfile(const char *name, const char *location, int namesize, namepadlen; unsigned int i; uint32_t csum = 0; - ssize_t this_read; mode |= S_IFREG; @@ -456,19 +455,9 @@ static int cpio_mkfile(const char *name, const char *location, push_pad(namepadlen ? namepadlen : padlen(offset, 4)) < 0) goto error; - if (size) { - this_read = copy_file_range(file, NULL, outfd, NULL, size, 0); - if (this_read > 0) { - if (this_read > size) - goto error; - offset += this_read; - size -= this_read; - } - /* short or failed copy falls back to read/write... */ - } - while (size) { unsigned char filebuf[65536]; + ssize_t this_read; size_t this_size = MIN(size, sizeof(filebuf)); this_read = read(file, filebuf, this_size); From dd7dd7e344406c0513f7c70c0d3de209b1bafc47 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Tue, 9 Sep 2025 17:14:53 +0800 Subject: [PATCH 002/110] macos: use gnu-sed instead of sed --- scripts/Makefile.vmlinux | 2 +- scripts/gdb/linux/Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/Makefile.vmlinux b/scripts/Makefile.vmlinux index 276c3134a563..9478e496e02d 100644 --- a/scripts/Makefile.vmlinux +++ b/scripts/Makefile.vmlinux @@ -113,7 +113,7 @@ vmlinux: vmlinux.unstripped FORCE # what kmod expects to parse. quiet_cmd_modules_builtin_modinfo = GEN $@ cmd_modules_builtin_modinfo = $(cmd_objcopy); \ - sed -i 's/\x00\+$$/\x00/g' $@; \ + gsed -i 's/\x00\+$$/\x00/g' $@; \ chmod -x $@ OBJCOPYFLAGS_modules.builtin.modinfo := -j .modinfo -O binary diff --git a/scripts/gdb/linux/Makefile b/scripts/gdb/linux/Makefile index fcd32fcf3ae0..babf4facfbb2 100644 --- a/scripts/gdb/linux/Makefile +++ b/scripts/gdb/linux/Makefile @@ -16,7 +16,7 @@ endif quiet_cmd_gen_constants_py = GEN $@ cmd_gen_constants_py = \ $(CPP) -E -x c -P $(c_flags) $< > $@ ;\ - sed -i '1,//d;' $@ + gsed -i '1,//d;' $@ always-y += constants.py $(obj)/constants.py: $(src)/constants.py.in FORCE From 6fe169bc1326dcb6464caa73a481ae8377c33328 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Wed, 9 Oct 2024 15:25:57 +0800 Subject: [PATCH 003/110] build on macOS with Apple Silicon The folling is main instructions: export PATH="$(brew --prefix make)/libexec/gnubin:$PATH" export PATH="$(brew --prefix llvm)/bin:$PATH" export PATH="$(brew --prefix lld)/bin:$PATH" # Eliminate compile errors of scripts/mod/file2alias.c export HOSTCFLAGS="-D_UUID_T -D__GETHOSTUUID_H" # Maybe the symlink is missed on macOS #ln -s ../../../scripts/syscall.tbl arch/arm64/tools/syscall_64.tbl #ln -s qcom,sm8550-dispcc.h include/dt-bindings/clock/qcom,sm8650-dispcc.h #make ARCH=arm64 LLVM=1 #make LLVM=1 menuconfig #make LLVM=1 -j8 Signed-off-by: Tianjia Zhang --- env_macos.sh | 29 + scripts/include/byteswap.h | 70 + scripts/include/elf.h | 4491 ++++++++++++++++++++++++++++++++++++ scripts/include/endian.h | 57 + 4 files changed, 4647 insertions(+) create mode 100755 env_macos.sh create mode 100644 scripts/include/byteswap.h create mode 100644 scripts/include/elf.h create mode 100644 scripts/include/endian.h diff --git a/env_macos.sh b/env_macos.sh new file mode 100755 index 000000000000..d628b9c9db7c --- /dev/null +++ b/env_macos.sh @@ -0,0 +1,29 @@ +#!/bin/sh + + +#brew install coreutils findutils gnu-sed gnu-tar grep llvm make pkg-config + + +export PATH="$(brew --prefix make)/libexec/gnubin:$PATH" +export PATH="$(brew --prefix llvm)/bin:$PATH" +export PATH="$(brew --prefix lld)/bin:$PATH" + + +# UUID_T and GETHOSTUUID_H Eliminate compile errors of scripts/mod/file2alias.c +# O_LARGEFILE Eliminate compile errors of usr/gen_init_cpio.c +export HOSTCFLAGS="-D_UUID_T -D__GETHOSTUUID_H -DO_LARGEFILE=0" + + +# Use gnu sed instead of builtin sed +alias sed='gsed' + + +# Maybe the symlink is missed on macOS +#ln -s ../../../scripts/syscall.tbl arch/arm64/tools/syscall_64.tbl +#ln -s qcom,sm8550-dispcc.h include/dt-bindings/clock/qcom,sm8650-dispcc.h + + +#make ARCH=arm64 LLVM=1 + +#make LLVM=1 menuconfig +#make LLVM=1 -j8 diff --git a/scripts/include/byteswap.h b/scripts/include/byteswap.h new file mode 100644 index 000000000000..d6a9c684c440 --- /dev/null +++ b/scripts/include/byteswap.h @@ -0,0 +1,70 @@ +#ifndef _BITS_BYTESWAP_H +#define _BITS_BYTESWAP_H 1 + +#include + +/* Swap bytes in 16-bit value. */ +#define __bswap_constant_16(x) \ + ((__uint16_t) ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8))) + +static __inline __uint16_t +__bswap_16 (__uint16_t __bsx) +{ +#if defined(__clang__) || defined(__GNUC__) + return __builtin_bswap16 (__bsx); +#else + return __bswap_constant_16 (__bsx); +#endif +} + +/* Swap bytes in 32-bit value. */ +#define __bswap_constant_32(x) \ + ((((x) & 0xff000000u) >> 24) | (((x) & 0x00ff0000u) >> 8) \ + | (((x) & 0x0000ff00u) << 8) | (((x) & 0x000000ffu) << 24)) + +static __inline __uint32_t +__bswap_32 (__uint32_t __bsx) +{ +#if defined(__clang__) || defined(__GNUC__) + return __builtin_bswap32 (__bsx); +#else + return __bswap_constant_32 (__bsx); +#endif +} + +/* Swap bytes in 64-bit value. */ +#define __bswap_constant_64(x) \ + ((((x) & 0xff00000000000000ull) >> 56) \ + | (((x) & 0x00ff000000000000ull) >> 40) \ + | (((x) & 0x0000ff0000000000ull) >> 24) \ + | (((x) & 0x000000ff00000000ull) >> 8) \ + | (((x) & 0x00000000ff000000ull) << 8) \ + | (((x) & 0x0000000000ff0000ull) << 24) \ + | (((x) & 0x000000000000ff00ull) << 40) \ + | (((x) & 0x00000000000000ffull) << 56)) + +__extension__ static __inline __uint64_t +__bswap_64 (__uint64_t __bsx) +{ +#if defined(__clang__) || defined(__GNUC__) + return __builtin_bswap64 (__bsx); +#else + return __bswap_constant_64 (__bsx); +#endif +} + + + +/* The following definitions must all be macros, otherwise some + of the possible optimizations are not possible. */ + +/* Return a value with both bytes in the 16 bit argument swapped. */ +#define bswap_16(x) __bswap_16 (x) + +/* Return a value with all bytes in the 32 bit argument swapped. */ +#define bswap_32(x) __bswap_32 (x) + +/* Return a value with all bytes in the 64 bit argument swapped. */ +#define bswap_64(x) __bswap_64 (x) + +#endif /* _BITS_BYTESWAP_H */ diff --git a/scripts/include/elf.h b/scripts/include/elf.h new file mode 100644 index 000000000000..33aea7f743b8 --- /dev/null +++ b/scripts/include/elf.h @@ -0,0 +1,4491 @@ +/* This file defines standard ELF types, structures, and macros. + Copyright (C) 1995-2024 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#ifndef _ELF_H +#define _ELF_H 1 + +/* Standard ELF types. */ + +#include + +/* Type for a 16-bit quantity. */ +typedef uint16_t Elf32_Half; +typedef uint16_t Elf64_Half; + +/* Types for signed and unsigned 32-bit quantities. */ +typedef uint32_t Elf32_Word; +typedef int32_t Elf32_Sword; +typedef uint32_t Elf64_Word; +typedef int32_t Elf64_Sword; + +/* Types for signed and unsigned 64-bit quantities. */ +typedef uint64_t Elf32_Xword; +typedef int64_t Elf32_Sxword; +typedef uint64_t Elf64_Xword; +typedef int64_t Elf64_Sxword; + +/* Type of addresses. */ +typedef uint32_t Elf32_Addr; +typedef uint64_t Elf64_Addr; + +/* Type of file offsets. */ +typedef uint32_t Elf32_Off; +typedef uint64_t Elf64_Off; + +/* Type for section indices, which are 16-bit quantities. */ +typedef uint16_t Elf32_Section; +typedef uint16_t Elf64_Section; + +/* Type for version symbol information. */ +typedef Elf32_Half Elf32_Versym; +typedef Elf64_Half Elf64_Versym; + + +/* The ELF file header. This appears at the start of every ELF file. */ + +#define EI_NIDENT (16) + +typedef struct +{ + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf32_Half e_type; /* Object file type */ + Elf32_Half e_machine; /* Architecture */ + Elf32_Word e_version; /* Object file version */ + Elf32_Addr e_entry; /* Entry point virtual address */ + Elf32_Off e_phoff; /* Program header table file offset */ + Elf32_Off e_shoff; /* Section header table file offset */ + Elf32_Word e_flags; /* Processor-specific flags */ + Elf32_Half e_ehsize; /* ELF header size in bytes */ + Elf32_Half e_phentsize; /* Program header table entry size */ + Elf32_Half e_phnum; /* Program header table entry count */ + Elf32_Half e_shentsize; /* Section header table entry size */ + Elf32_Half e_shnum; /* Section header table entry count */ + Elf32_Half e_shstrndx; /* Section header string table index */ +} Elf32_Ehdr; + +typedef struct +{ + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf64_Half e_type; /* Object file type */ + Elf64_Half e_machine; /* Architecture */ + Elf64_Word e_version; /* Object file version */ + Elf64_Addr e_entry; /* Entry point virtual address */ + Elf64_Off e_phoff; /* Program header table file offset */ + Elf64_Off e_shoff; /* Section header table file offset */ + Elf64_Word e_flags; /* Processor-specific flags */ + Elf64_Half e_ehsize; /* ELF header size in bytes */ + Elf64_Half e_phentsize; /* Program header table entry size */ + Elf64_Half e_phnum; /* Program header table entry count */ + Elf64_Half e_shentsize; /* Section header table entry size */ + Elf64_Half e_shnum; /* Section header table entry count */ + Elf64_Half e_shstrndx; /* Section header string table index */ +} Elf64_Ehdr; + +/* Fields in the e_ident array. The EI_* macros are indices into the + array. The macros under each EI_* macro are the values the byte + may have. */ + +#define EI_MAG0 0 /* File identification byte 0 index */ +#define ELFMAG0 0x7f /* Magic number byte 0 */ + +#define EI_MAG1 1 /* File identification byte 1 index */ +#define ELFMAG1 'E' /* Magic number byte 1 */ + +#define EI_MAG2 2 /* File identification byte 2 index */ +#define ELFMAG2 'L' /* Magic number byte 2 */ + +#define EI_MAG3 3 /* File identification byte 3 index */ +#define ELFMAG3 'F' /* Magic number byte 3 */ + +/* Conglomeration of the identification bytes, for easy testing as a word. */ +#define ELFMAG "\177ELF" +#define SELFMAG 4 + +#define EI_CLASS 4 /* File class byte index */ +#define ELFCLASSNONE 0 /* Invalid class */ +#define ELFCLASS32 1 /* 32-bit objects */ +#define ELFCLASS64 2 /* 64-bit objects */ +#define ELFCLASSNUM 3 + +#define EI_DATA 5 /* Data encoding byte index */ +#define ELFDATANONE 0 /* Invalid data encoding */ +#define ELFDATA2LSB 1 /* 2's complement, little endian */ +#define ELFDATA2MSB 2 /* 2's complement, big endian */ +#define ELFDATANUM 3 + +#define EI_VERSION 6 /* File version byte index */ + /* Value must be EV_CURRENT */ + +#define EI_OSABI 7 /* OS ABI identification */ +#define ELFOSABI_NONE 0 /* UNIX System V ABI */ +#define ELFOSABI_SYSV 0 /* Alias. */ +#define ELFOSABI_HPUX 1 /* HP-UX */ +#define ELFOSABI_NETBSD 2 /* NetBSD. */ +#define ELFOSABI_GNU 3 /* Object uses GNU ELF extensions. */ +#define ELFOSABI_LINUX ELFOSABI_GNU /* Compatibility alias. */ +#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ +#define ELFOSABI_AIX 7 /* IBM AIX. */ +#define ELFOSABI_IRIX 8 /* SGI Irix. */ +#define ELFOSABI_FREEBSD 9 /* FreeBSD. */ +#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ +#define ELFOSABI_MODESTO 11 /* Novell Modesto. */ +#define ELFOSABI_OPENBSD 12 /* OpenBSD. */ +#define ELFOSABI_ARM_AEABI 64 /* ARM EABI */ +#define ELFOSABI_ARM 97 /* ARM */ +#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ + +#define EI_ABIVERSION 8 /* ABI version */ + +#define EI_PAD 9 /* Byte index of padding bytes */ + +/* Legal values for e_type (object file type). */ + +#define ET_NONE 0 /* No file type */ +#define ET_REL 1 /* Relocatable file */ +#define ET_EXEC 2 /* Executable file */ +#define ET_DYN 3 /* Shared object file */ +#define ET_CORE 4 /* Core file */ +#define ET_NUM 5 /* Number of defined types */ +#define ET_LOOS 0xfe00 /* OS-specific range start */ +#define ET_HIOS 0xfeff /* OS-specific range end */ +#define ET_LOPROC 0xff00 /* Processor-specific range start */ +#define ET_HIPROC 0xffff /* Processor-specific range end */ + +/* Legal values for e_machine (architecture). */ + +#define EM_NONE 0 /* No machine */ +#define EM_M32 1 /* AT&T WE 32100 */ +#define EM_SPARC 2 /* SUN SPARC */ +#define EM_386 3 /* Intel 80386 */ +#define EM_68K 4 /* Motorola m68k family */ +#define EM_88K 5 /* Motorola m88k family */ +#define EM_IAMCU 6 /* Intel MCU */ +#define EM_860 7 /* Intel 80860 */ +#define EM_MIPS 8 /* MIPS R3000 big-endian */ +#define EM_S370 9 /* IBM System/370 */ +#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ + /* reserved 11-14 */ +#define EM_PARISC 15 /* HPPA */ + /* reserved 16 */ +#define EM_VPP500 17 /* Fujitsu VPP500 */ +#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ +#define EM_960 19 /* Intel 80960 */ +#define EM_PPC 20 /* PowerPC */ +#define EM_PPC64 21 /* PowerPC 64-bit */ +#define EM_S390 22 /* IBM S390 */ +#define EM_SPU 23 /* IBM SPU/SPC */ + /* reserved 24-35 */ +#define EM_V800 36 /* NEC V800 series */ +#define EM_FR20 37 /* Fujitsu FR20 */ +#define EM_RH32 38 /* TRW RH-32 */ +#define EM_RCE 39 /* Motorola RCE */ +#define EM_ARM 40 /* ARM */ +#define EM_FAKE_ALPHA 41 /* Digital Alpha */ +#define EM_SH 42 /* Hitachi SH */ +#define EM_SPARCV9 43 /* SPARC v9 64-bit */ +#define EM_TRICORE 44 /* Siemens Tricore */ +#define EM_ARC 45 /* Argonaut RISC Core */ +#define EM_H8_300 46 /* Hitachi H8/300 */ +#define EM_H8_300H 47 /* Hitachi H8/300H */ +#define EM_H8S 48 /* Hitachi H8S */ +#define EM_H8_500 49 /* Hitachi H8/500 */ +#define EM_IA_64 50 /* Intel Merced */ +#define EM_MIPS_X 51 /* Stanford MIPS-X */ +#define EM_COLDFIRE 52 /* Motorola Coldfire */ +#define EM_68HC12 53 /* Motorola M68HC12 */ +#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator */ +#define EM_PCP 55 /* Siemens PCP */ +#define EM_NCPU 56 /* Sony nCPU embedded RISC */ +#define EM_NDR1 57 /* Denso NDR1 microprocessor */ +#define EM_STARCORE 58 /* Motorola Start*Core processor */ +#define EM_ME16 59 /* Toyota ME16 processor */ +#define EM_ST100 60 /* STMicroelectronic ST100 processor */ +#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam */ +#define EM_X86_64 62 /* AMD x86-64 architecture */ +#define EM_PDSP 63 /* Sony DSP Processor */ +#define EM_PDP10 64 /* Digital PDP-10 */ +#define EM_PDP11 65 /* Digital PDP-11 */ +#define EM_FX66 66 /* Siemens FX66 microcontroller */ +#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ +#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ +#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ +#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ +#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ +#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ +#define EM_SVX 73 /* Silicon Graphics SVx */ +#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ +#define EM_VAX 75 /* Digital VAX */ +#define EM_CRIS 76 /* Axis Communications 32-bit emb.proc */ +#define EM_JAVELIN 77 /* Infineon Technologies 32-bit emb.proc */ +#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ +#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ +#define EM_MMIX 80 /* Donald Knuth's educational 64-bit proc */ +#define EM_HUANY 81 /* Harvard University machine-independent object files */ +#define EM_PRISM 82 /* SiTera Prism */ +#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ +#define EM_FR30 84 /* Fujitsu FR30 */ +#define EM_D10V 85 /* Mitsubishi D10V */ +#define EM_D30V 86 /* Mitsubishi D30V */ +#define EM_V850 87 /* NEC v850 */ +#define EM_M32R 88 /* Mitsubishi M32R */ +#define EM_MN10300 89 /* Matsushita MN10300 */ +#define EM_MN10200 90 /* Matsushita MN10200 */ +#define EM_PJ 91 /* picoJava */ +#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ +#define EM_ARC_COMPACT 93 /* ARC International ARCompact */ +#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ +#define EM_VIDEOCORE 95 /* Alphamosaic VideoCore */ +#define EM_TMM_GPP 96 /* Thompson Multimedia General Purpose Proc */ +#define EM_NS32K 97 /* National Semi. 32000 */ +#define EM_TPC 98 /* Tenor Network TPC */ +#define EM_SNP1K 99 /* Trebia SNP 1000 */ +#define EM_ST200 100 /* STMicroelectronics ST200 */ +#define EM_IP2K 101 /* Ubicom IP2xxx */ +#define EM_MAX 102 /* MAX processor */ +#define EM_CR 103 /* National Semi. CompactRISC */ +#define EM_F2MC16 104 /* Fujitsu F2MC16 */ +#define EM_MSP430 105 /* Texas Instruments msp430 */ +#define EM_BLACKFIN 106 /* Analog Devices Blackfin DSP */ +#define EM_SE_C33 107 /* Seiko Epson S1C33 family */ +#define EM_SEP 108 /* Sharp embedded microprocessor */ +#define EM_ARCA 109 /* Arca RISC */ +#define EM_UNICORE 110 /* PKU-Unity & MPRC Peking Uni. mc series */ +#define EM_EXCESS 111 /* eXcess configurable cpu */ +#define EM_DXP 112 /* Icera Semi. Deep Execution Processor */ +#define EM_ALTERA_NIOS2 113 /* Altera Nios II */ +#define EM_CRX 114 /* National Semi. CompactRISC CRX */ +#define EM_XGATE 115 /* Motorola XGATE */ +#define EM_C166 116 /* Infineon C16x/XC16x */ +#define EM_M16C 117 /* Renesas M16C */ +#define EM_DSPIC30F 118 /* Microchip Technology dsPIC30F */ +#define EM_CE 119 /* Freescale Communication Engine RISC */ +#define EM_M32C 120 /* Renesas M32C */ + /* reserved 121-130 */ +#define EM_TSK3000 131 /* Altium TSK3000 */ +#define EM_RS08 132 /* Freescale RS08 */ +#define EM_SHARC 133 /* Analog Devices SHARC family */ +#define EM_ECOG2 134 /* Cyan Technology eCOG2 */ +#define EM_SCORE7 135 /* Sunplus S+core7 RISC */ +#define EM_DSP24 136 /* New Japan Radio (NJR) 24-bit DSP */ +#define EM_VIDEOCORE3 137 /* Broadcom VideoCore III */ +#define EM_LATTICEMICO32 138 /* RISC for Lattice FPGA */ +#define EM_SE_C17 139 /* Seiko Epson C17 */ +#define EM_TI_C6000 140 /* Texas Instruments TMS320C6000 DSP */ +#define EM_TI_C2000 141 /* Texas Instruments TMS320C2000 DSP */ +#define EM_TI_C5500 142 /* Texas Instruments TMS320C55x DSP */ +#define EM_TI_ARP32 143 /* Texas Instruments App. Specific RISC */ +#define EM_TI_PRU 144 /* Texas Instruments Prog. Realtime Unit */ + /* reserved 145-159 */ +#define EM_MMDSP_PLUS 160 /* STMicroelectronics 64bit VLIW DSP */ +#define EM_CYPRESS_M8C 161 /* Cypress M8C */ +#define EM_R32C 162 /* Renesas R32C */ +#define EM_TRIMEDIA 163 /* NXP Semi. TriMedia */ +#define EM_QDSP6 164 /* QUALCOMM DSP6 */ +#define EM_8051 165 /* Intel 8051 and variants */ +#define EM_STXP7X 166 /* STMicroelectronics STxP7x */ +#define EM_NDS32 167 /* Andes Tech. compact code emb. RISC */ +#define EM_ECOG1X 168 /* Cyan Technology eCOG1X */ +#define EM_MAXQ30 169 /* Dallas Semi. MAXQ30 mc */ +#define EM_XIMO16 170 /* New Japan Radio (NJR) 16-bit DSP */ +#define EM_MANIK 171 /* M2000 Reconfigurable RISC */ +#define EM_CRAYNV2 172 /* Cray NV2 vector architecture */ +#define EM_RX 173 /* Renesas RX */ +#define EM_METAG 174 /* Imagination Tech. META */ +#define EM_MCST_ELBRUS 175 /* MCST Elbrus */ +#define EM_ECOG16 176 /* Cyan Technology eCOG16 */ +#define EM_CR16 177 /* National Semi. CompactRISC CR16 */ +#define EM_ETPU 178 /* Freescale Extended Time Processing Unit */ +#define EM_SLE9X 179 /* Infineon Tech. SLE9X */ +#define EM_L10M 180 /* Intel L10M */ +#define EM_K10M 181 /* Intel K10M */ + /* reserved 182 */ +#define EM_AARCH64 183 /* ARM AARCH64 */ + /* reserved 184 */ +#define EM_AVR32 185 /* Amtel 32-bit microprocessor */ +#define EM_STM8 186 /* STMicroelectronics STM8 */ +#define EM_TILE64 187 /* Tilera TILE64 */ +#define EM_TILEPRO 188 /* Tilera TILEPro */ +#define EM_MICROBLAZE 189 /* Xilinx MicroBlaze */ +#define EM_CUDA 190 /* NVIDIA CUDA */ +#define EM_TILEGX 191 /* Tilera TILE-Gx */ +#define EM_CLOUDSHIELD 192 /* CloudShield */ +#define EM_COREA_1ST 193 /* KIPO-KAIST Core-A 1st gen. */ +#define EM_COREA_2ND 194 /* KIPO-KAIST Core-A 2nd gen. */ +#define EM_ARCV2 195 /* Synopsys ARCv2 ISA. */ +#define EM_OPEN8 196 /* Open8 RISC */ +#define EM_RL78 197 /* Renesas RL78 */ +#define EM_VIDEOCORE5 198 /* Broadcom VideoCore V */ +#define EM_78KOR 199 /* Renesas 78KOR */ +#define EM_56800EX 200 /* Freescale 56800EX DSC */ +#define EM_BA1 201 /* Beyond BA1 */ +#define EM_BA2 202 /* Beyond BA2 */ +#define EM_XCORE 203 /* XMOS xCORE */ +#define EM_MCHP_PIC 204 /* Microchip 8-bit PIC(r) */ +#define EM_INTELGT 205 /* Intel Graphics Technology */ + /* reserved 206-209 */ +#define EM_KM32 210 /* KM211 KM32 */ +#define EM_KMX32 211 /* KM211 KMX32 */ +#define EM_EMX16 212 /* KM211 KMX16 */ +#define EM_EMX8 213 /* KM211 KMX8 */ +#define EM_KVARC 214 /* KM211 KVARC */ +#define EM_CDP 215 /* Paneve CDP */ +#define EM_COGE 216 /* Cognitive Smart Memory Processor */ +#define EM_COOL 217 /* Bluechip CoolEngine */ +#define EM_NORC 218 /* Nanoradio Optimized RISC */ +#define EM_CSR_KALIMBA 219 /* CSR Kalimba */ +#define EM_Z80 220 /* Zilog Z80 */ +#define EM_VISIUM 221 /* Controls and Data Services VISIUMcore */ +#define EM_FT32 222 /* FTDI Chip FT32 */ +#define EM_MOXIE 223 /* Moxie processor */ +#define EM_AMDGPU 224 /* AMD GPU */ + /* reserved 225-242 */ +#define EM_RISCV 243 /* RISC-V */ + +#define EM_BPF 247 /* Linux BPF -- in-kernel virtual machine */ +#define EM_CSKY 252 /* C-SKY */ +#define EM_LOONGARCH 258 /* LoongArch */ + +#define EM_NUM 259 + +/* Old spellings/synonyms. */ + +#define EM_ARC_A5 EM_ARC_COMPACT + +/* If it is necessary to assign new unofficial EM_* values, please + pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the + chances of collision with official or non-GNU unofficial values. */ + +#define EM_ALPHA 0x9026 + +/* Legal values for e_version (version). */ + +#define EV_NONE 0 /* Invalid ELF version */ +#define EV_CURRENT 1 /* Current version */ +#define EV_NUM 2 + +/* Section header. */ + +typedef struct +{ + Elf32_Word sh_name; /* Section name (string tbl index) */ + Elf32_Word sh_type; /* Section type */ + Elf32_Word sh_flags; /* Section flags */ + Elf32_Addr sh_addr; /* Section virtual addr at execution */ + Elf32_Off sh_offset; /* Section file offset */ + Elf32_Word sh_size; /* Section size in bytes */ + Elf32_Word sh_link; /* Link to another section */ + Elf32_Word sh_info; /* Additional section information */ + Elf32_Word sh_addralign; /* Section alignment */ + Elf32_Word sh_entsize; /* Entry size if section holds table */ +} Elf32_Shdr; + +typedef struct +{ + Elf64_Word sh_name; /* Section name (string tbl index) */ + Elf64_Word sh_type; /* Section type */ + Elf64_Xword sh_flags; /* Section flags */ + Elf64_Addr sh_addr; /* Section virtual addr at execution */ + Elf64_Off sh_offset; /* Section file offset */ + Elf64_Xword sh_size; /* Section size in bytes */ + Elf64_Word sh_link; /* Link to another section */ + Elf64_Word sh_info; /* Additional section information */ + Elf64_Xword sh_addralign; /* Section alignment */ + Elf64_Xword sh_entsize; /* Entry size if section holds table */ +} Elf64_Shdr; + +/* Special section indices. */ + +#define SHN_UNDEF 0 /* Undefined section */ +#define SHN_LORESERVE 0xff00 /* Start of reserved indices */ +#define SHN_LOPROC 0xff00 /* Start of processor-specific */ +#define SHN_BEFORE 0xff00 /* Order section before all others + (Solaris). */ +#define SHN_AFTER 0xff01 /* Order section after all others + (Solaris). */ +#define SHN_HIPROC 0xff1f /* End of processor-specific */ +#define SHN_LOOS 0xff20 /* Start of OS-specific */ +#define SHN_HIOS 0xff3f /* End of OS-specific */ +#define SHN_ABS 0xfff1 /* Associated symbol is absolute */ +#define SHN_COMMON 0xfff2 /* Associated symbol is common */ +#define SHN_XINDEX 0xffff /* Index is in extra table. */ +#define SHN_HIRESERVE 0xffff /* End of reserved indices */ + +/* Legal values for sh_type (section type). */ + +#define SHT_NULL 0 /* Section header table entry unused */ +#define SHT_PROGBITS 1 /* Program data */ +#define SHT_SYMTAB 2 /* Symbol table */ +#define SHT_STRTAB 3 /* String table */ +#define SHT_RELA 4 /* Relocation entries with addends */ +#define SHT_HASH 5 /* Symbol hash table */ +#define SHT_DYNAMIC 6 /* Dynamic linking information */ +#define SHT_NOTE 7 /* Notes */ +#define SHT_NOBITS 8 /* Program space with no data (bss) */ +#define SHT_REL 9 /* Relocation entries, no addends */ +#define SHT_SHLIB 10 /* Reserved */ +#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ +#define SHT_INIT_ARRAY 14 /* Array of constructors */ +#define SHT_FINI_ARRAY 15 /* Array of destructors */ +#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ +#define SHT_GROUP 17 /* Section group */ +#define SHT_SYMTAB_SHNDX 18 /* Extended section indices */ +#define SHT_RELR 19 /* RELR relative relocations */ +#define SHT_NUM 20 /* Number of defined types. */ +#define SHT_LOOS 0x60000000 /* Start OS-specific. */ +#define SHT_GNU_ATTRIBUTES 0x6ffffff5 /* Object attributes. */ +#define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table. */ +#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */ +#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */ +#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */ +#define SHT_SUNW_move 0x6ffffffa +#define SHT_SUNW_COMDAT 0x6ffffffb +#define SHT_SUNW_syminfo 0x6ffffffc +#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */ +#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */ +#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */ +#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ +#define SHT_HIOS 0x6fffffff /* End OS-specific type */ +#define SHT_LOPROC 0x70000000 /* Start of processor-specific */ +#define SHT_HIPROC 0x7fffffff /* End of processor-specific */ +#define SHT_LOUSER 0x80000000 /* Start of application-specific */ +#define SHT_HIUSER 0x8fffffff /* End of application-specific */ + +/* Legal values for sh_flags (section flags). */ + +#define SHF_WRITE (1 << 0) /* Writable */ +#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ +#define SHF_EXECINSTR (1 << 2) /* Executable */ +#define SHF_MERGE (1 << 4) /* Might be merged */ +#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */ +#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */ +#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */ +#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling + required */ +#define SHF_GROUP (1 << 9) /* Section is member of a group. */ +#define SHF_TLS (1 << 10) /* Section hold thread-local data. */ +#define SHF_COMPRESSED (1 << 11) /* Section with compressed data. */ +#define SHF_MASKOS 0x0ff00000 /* OS-specific. */ +#define SHF_MASKPROC 0xf0000000 /* Processor-specific */ +#define SHF_GNU_RETAIN (1 << 21) /* Not to be GCed by linker. */ +#define SHF_ORDERED (1 << 30) /* Special ordering requirement + (Solaris). */ +#define SHF_EXCLUDE (1U << 31) /* Section is excluded unless + referenced or allocated (Solaris).*/ + +/* Section compression header. Used when SHF_COMPRESSED is set. */ + +typedef struct +{ + Elf32_Word ch_type; /* Compression format. */ + Elf32_Word ch_size; /* Uncompressed data size. */ + Elf32_Word ch_addralign; /* Uncompressed data alignment. */ +} Elf32_Chdr; + +typedef struct +{ + Elf64_Word ch_type; /* Compression format. */ + Elf64_Word ch_reserved; + Elf64_Xword ch_size; /* Uncompressed data size. */ + Elf64_Xword ch_addralign; /* Uncompressed data alignment. */ +} Elf64_Chdr; + +/* Legal values for ch_type (compression algorithm). */ +#define ELFCOMPRESS_ZLIB 1 /* ZLIB/DEFLATE algorithm. */ +#define ELFCOMPRESS_ZSTD 2 /* Zstandard algorithm. */ +#define ELFCOMPRESS_LOOS 0x60000000 /* Start of OS-specific. */ +#define ELFCOMPRESS_HIOS 0x6fffffff /* End of OS-specific. */ +#define ELFCOMPRESS_LOPROC 0x70000000 /* Start of processor-specific. */ +#define ELFCOMPRESS_HIPROC 0x7fffffff /* End of processor-specific. */ + +/* Section group handling. */ +#define GRP_COMDAT 0x1 /* Mark group as COMDAT. */ + +/* Symbol table entry. */ + +typedef struct +{ + Elf32_Word st_name; /* Symbol name (string tbl index) */ + Elf32_Addr st_value; /* Symbol value */ + Elf32_Word st_size; /* Symbol size */ + unsigned char st_info; /* Symbol type and binding */ + unsigned char st_other; /* Symbol visibility */ + Elf32_Section st_shndx; /* Section index */ +} Elf32_Sym; + +typedef struct +{ + Elf64_Word st_name; /* Symbol name (string tbl index) */ + unsigned char st_info; /* Symbol type and binding */ + unsigned char st_other; /* Symbol visibility */ + Elf64_Section st_shndx; /* Section index */ + Elf64_Addr st_value; /* Symbol value */ + Elf64_Xword st_size; /* Symbol size */ +} Elf64_Sym; + +/* The syminfo section if available contains additional information about + every dynamic symbol. */ + +typedef struct +{ + Elf32_Half si_boundto; /* Direct bindings, symbol bound to */ + Elf32_Half si_flags; /* Per symbol flags */ +} Elf32_Syminfo; + +typedef struct +{ + Elf64_Half si_boundto; /* Direct bindings, symbol bound to */ + Elf64_Half si_flags; /* Per symbol flags */ +} Elf64_Syminfo; + +/* Possible values for si_boundto. */ +#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */ +#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */ +#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */ + +/* Possible bitmasks for si_flags. */ +#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */ +#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-through symbol for translator */ +#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */ +#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy + loaded */ +/* Syminfo version values. */ +#define SYMINFO_NONE 0 +#define SYMINFO_CURRENT 1 +#define SYMINFO_NUM 2 + + +/* How to extract and insert information held in the st_info field. */ + +#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) +#define ELF32_ST_TYPE(val) ((val) & 0xf) +#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) + +/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */ +#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) +#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) +#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) + +/* Legal values for ST_BIND subfield of st_info (symbol binding). */ + +#define STB_LOCAL 0 /* Local symbol */ +#define STB_GLOBAL 1 /* Global symbol */ +#define STB_WEAK 2 /* Weak symbol */ +#define STB_NUM 3 /* Number of defined types. */ +#define STB_LOOS 10 /* Start of OS-specific */ +#define STB_GNU_UNIQUE 10 /* Unique symbol. */ +#define STB_HIOS 12 /* End of OS-specific */ +#define STB_LOPROC 13 /* Start of processor-specific */ +#define STB_HIPROC 15 /* End of processor-specific */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_NOTYPE 0 /* Symbol type is unspecified */ +#define STT_OBJECT 1 /* Symbol is a data object */ +#define STT_FUNC 2 /* Symbol is a code object */ +#define STT_SECTION 3 /* Symbol associated with a section */ +#define STT_FILE 4 /* Symbol's name is file name */ +#define STT_COMMON 5 /* Symbol is a common data object */ +#define STT_TLS 6 /* Symbol is thread-local data object*/ +#define STT_NUM 7 /* Number of defined types. */ +#define STT_LOOS 10 /* Start of OS-specific */ +#define STT_GNU_IFUNC 10 /* Symbol is indirect code object */ +#define STT_HIOS 12 /* End of OS-specific */ +#define STT_LOPROC 13 /* Start of processor-specific */ +#define STT_HIPROC 15 /* End of processor-specific */ + + +/* Symbol table indices are found in the hash buckets and chain table + of a symbol hash table section. This special index value indicates + the end of a chain, meaning no further symbols are found in that bucket. */ + +#define STN_UNDEF 0 /* End of a chain. */ + + +/* How to extract and insert information held in the st_other field. */ + +#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) + +/* For ELF64 the definitions are the same. */ +#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) + +/* Symbol visibility specification encoded in the st_other field. */ +#define STV_DEFAULT 0 /* Default symbol visibility rules */ +#define STV_INTERNAL 1 /* Processor specific hidden class */ +#define STV_HIDDEN 2 /* Sym unavailable in other modules */ +#define STV_PROTECTED 3 /* Not preemptible, not exported */ + + +/* Relocation table entry without addend (in section of type SHT_REL). */ + +typedef struct +{ + Elf32_Addr r_offset; /* Address */ + Elf32_Word r_info; /* Relocation type and symbol index */ +} Elf32_Rel; + +/* I have seen two different definitions of the Elf64_Rel and + Elf64_Rela structures, so we'll leave them out until Novell (or + whoever) gets their act together. */ +/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */ + +typedef struct +{ + Elf64_Addr r_offset; /* Address */ + Elf64_Xword r_info; /* Relocation type and symbol index */ +} Elf64_Rel; + +/* Relocation table entry with addend (in section of type SHT_RELA). */ + +typedef struct +{ + Elf32_Addr r_offset; /* Address */ + Elf32_Word r_info; /* Relocation type and symbol index */ + Elf32_Sword r_addend; /* Addend */ +} Elf32_Rela; + +typedef struct +{ + Elf64_Addr r_offset; /* Address */ + Elf64_Xword r_info; /* Relocation type and symbol index */ + Elf64_Sxword r_addend; /* Addend */ +} Elf64_Rela; + +/* RELR relocation table entry */ + +typedef Elf32_Word Elf32_Relr; +typedef Elf64_Xword Elf64_Relr; + +/* How to extract and insert information held in the r_info field. */ + +#define ELF32_R_SYM(val) ((val) >> 8) +#define ELF32_R_TYPE(val) ((val) & 0xff) +#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) + +#define ELF64_R_SYM(i) ((i) >> 32) +#define ELF64_R_TYPE(i) ((i) & 0xffffffff) +#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) + +/* Program segment header. */ + +typedef struct +{ + Elf32_Word p_type; /* Segment type */ + Elf32_Off p_offset; /* Segment file offset */ + Elf32_Addr p_vaddr; /* Segment virtual address */ + Elf32_Addr p_paddr; /* Segment physical address */ + Elf32_Word p_filesz; /* Segment size in file */ + Elf32_Word p_memsz; /* Segment size in memory */ + Elf32_Word p_flags; /* Segment flags */ + Elf32_Word p_align; /* Segment alignment */ +} Elf32_Phdr; + +typedef struct +{ + Elf64_Word p_type; /* Segment type */ + Elf64_Word p_flags; /* Segment flags */ + Elf64_Off p_offset; /* Segment file offset */ + Elf64_Addr p_vaddr; /* Segment virtual address */ + Elf64_Addr p_paddr; /* Segment physical address */ + Elf64_Xword p_filesz; /* Segment size in file */ + Elf64_Xword p_memsz; /* Segment size in memory */ + Elf64_Xword p_align; /* Segment alignment */ +} Elf64_Phdr; + +/* Special value for e_phnum. This indicates that the real number of + program headers is too large to fit into e_phnum. Instead the real + value is in the field sh_info of section 0. */ + +#define PN_XNUM 0xffff + +/* Legal values for p_type (segment type). */ + +#define PT_NULL 0 /* Program header table entry unused */ +#define PT_LOAD 1 /* Loadable program segment */ +#define PT_DYNAMIC 2 /* Dynamic linking information */ +#define PT_INTERP 3 /* Program interpreter */ +#define PT_NOTE 4 /* Auxiliary information */ +#define PT_SHLIB 5 /* Reserved */ +#define PT_PHDR 6 /* Entry for header table itself */ +#define PT_TLS 7 /* Thread-local storage segment */ +#define PT_NUM 8 /* Number of defined types */ +#define PT_LOOS 0x60000000 /* Start of OS-specific */ +#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ +#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ +#define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */ +#define PT_GNU_PROPERTY 0x6474e553 /* GNU property */ +#define PT_GNU_SFRAME 0x6474e554 /* SFrame segment. */ +#define PT_LOSUNW 0x6ffffffa +#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ +#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ +#define PT_HISUNW 0x6fffffff +#define PT_HIOS 0x6fffffff /* End of OS-specific */ +#define PT_LOPROC 0x70000000 /* Start of processor-specific */ +#define PT_HIPROC 0x7fffffff /* End of processor-specific */ + +/* Legal values for p_flags (segment flags). */ + +#define PF_X (1 << 0) /* Segment is executable */ +#define PF_W (1 << 1) /* Segment is writable */ +#define PF_R (1 << 2) /* Segment is readable */ +#define PF_MASKOS 0x0ff00000 /* OS-specific */ +#define PF_MASKPROC 0xf0000000 /* Processor-specific */ + +/* Legal values for note segment descriptor types for core files. */ + +#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ +#define NT_PRFPREG 2 /* Contains copy of fpregset + struct. */ +#define NT_FPREGSET 2 /* Contains copy of fpregset struct */ +#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ +#define NT_PRXREG 4 /* Contains copy of prxregset struct */ +#define NT_TASKSTRUCT 4 /* Contains copy of task structure */ +#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */ +#define NT_AUXV 6 /* Contains copy of auxv array */ +#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */ +#define NT_ASRS 8 /* Contains copy of asrset struct */ +#define NT_PSTATUS 10 /* Contains copy of pstatus struct */ +#define NT_PSINFO 13 /* Contains copy of psinfo struct */ +#define NT_PRCRED 14 /* Contains copy of prcred struct */ +#define NT_UTSNAME 15 /* Contains copy of utsname struct */ +#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */ +#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */ +#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct */ +#define NT_SIGINFO 0x53494749 /* Contains copy of siginfo_t, + size might increase */ +#define NT_FILE 0x46494c45 /* Contains information about mapped + files */ +#define NT_PRXFPREG 0x46e62b7f /* Contains copy of user_fxsr_struct */ +#define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */ +#define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */ +#define NT_PPC_VSX 0x102 /* PowerPC VSX registers */ +#define NT_PPC_TAR 0x103 /* Target Address Register */ +#define NT_PPC_PPR 0x104 /* Program Priority Register */ +#define NT_PPC_DSCR 0x105 /* Data Stream Control Register */ +#define NT_PPC_EBB 0x106 /* Event Based Branch Registers */ +#define NT_PPC_PMU 0x107 /* Performance Monitor Registers */ +#define NT_PPC_TM_CGPR 0x108 /* TM checkpointed GPR Registers */ +#define NT_PPC_TM_CFPR 0x109 /* TM checkpointed FPR Registers */ +#define NT_PPC_TM_CVMX 0x10a /* TM checkpointed VMX Registers */ +#define NT_PPC_TM_CVSX 0x10b /* TM checkpointed VSX Registers */ +#define NT_PPC_TM_SPR 0x10c /* TM Special Purpose Registers */ +#define NT_PPC_TM_CTAR 0x10d /* TM checkpointed Target Address + Register */ +#define NT_PPC_TM_CPPR 0x10e /* TM checkpointed Program Priority + Register */ +#define NT_PPC_TM_CDSCR 0x10f /* TM checkpointed Data Stream Control + Register */ +#define NT_PPC_PKEY 0x110 /* Memory Protection Keys + registers. */ +#define NT_PPC_DEXCR 0x111 /* PowerPC DEXCR registers. */ +#define NT_PPC_HASHKEYR 0x112 /* PowerPC HASHKEYR register. */ +#define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */ +#define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */ +#define NT_X86_XSTATE 0x202 /* x86 extended state using xsave */ +#define NT_X86_SHSTK 0x204 /* x86 SHSTK state */ +#define NT_S390_HIGH_GPRS 0x300 /* s390 upper register halves */ +#define NT_S390_TIMER 0x301 /* s390 timer register */ +#define NT_S390_TODCMP 0x302 /* s390 TOD clock comparator register */ +#define NT_S390_TODPREG 0x303 /* s390 TOD programmable register */ +#define NT_S390_CTRS 0x304 /* s390 control registers */ +#define NT_S390_PREFIX 0x305 /* s390 prefix register */ +#define NT_S390_LAST_BREAK 0x306 /* s390 breaking event address */ +#define NT_S390_SYSTEM_CALL 0x307 /* s390 system call restart data */ +#define NT_S390_TDB 0x308 /* s390 transaction diagnostic block */ +#define NT_S390_VXRS_LOW 0x309 /* s390 vector registers 0-15 + upper half. */ +#define NT_S390_VXRS_HIGH 0x30a /* s390 vector registers 16-31. */ +#define NT_S390_GS_CB 0x30b /* s390 guarded storage registers. */ +#define NT_S390_GS_BC 0x30c /* s390 guarded storage + broadcast control block. */ +#define NT_S390_RI_CB 0x30d /* s390 runtime instrumentation. */ +#define NT_S390_PV_CPU_DATA 0x30e /* s390 protvirt cpu dump data. */ +#define NT_ARM_VFP 0x400 /* ARM VFP/NEON registers */ +#define NT_ARM_TLS 0x401 /* ARM TLS register */ +#define NT_ARM_HW_BREAK 0x402 /* ARM hardware breakpoint registers */ +#define NT_ARM_HW_WATCH 0x403 /* ARM hardware watchpoint registers */ +#define NT_ARM_SYSTEM_CALL 0x404 /* ARM system call number */ +#define NT_ARM_SVE 0x405 /* ARM Scalable Vector Extension + registers */ +#define NT_ARM_PAC_MASK 0x406 /* ARM pointer authentication + code masks. */ +#define NT_ARM_PACA_KEYS 0x407 /* ARM pointer authentication + address keys. */ +#define NT_ARM_PACG_KEYS 0x408 /* ARM pointer authentication + generic key. */ +#define NT_ARM_TAGGED_ADDR_CTRL 0x409 /* AArch64 tagged address + control. */ +#define NT_ARM_PAC_ENABLED_KEYS 0x40a /* AArch64 pointer authentication + enabled keys. */ +#define NT_ARM_SSVE 0x40b /* ARM Streaming SVE registers. */ +#define NT_ARM_ZA 0x40c /* ARM SME ZA registers. */ +#define NT_ARM_ZT 0x40d /* ARM SME ZT registers. */ +#define NT_ARM_FPMR 0x40e /* ARM floating point mode register. */ +#define NT_VMCOREDD 0x700 /* Vmcore Device Dump Note. */ +#define NT_MIPS_DSP 0x800 /* MIPS DSP ASE registers. */ +#define NT_MIPS_FP_MODE 0x801 /* MIPS floating-point mode. */ +#define NT_MIPS_MSA 0x802 /* MIPS SIMD registers. */ +#define NT_RISCV_CSR 0x900 /* RISC-V Control and Status Registers */ +#define NT_RISCV_VECTOR 0x901 /* RISC-V vector registers */ +#define NT_LOONGARCH_CPUCFG 0xa00 /* LoongArch CPU config registers. */ +#define NT_LOONGARCH_CSR 0xa01 /* LoongArch control and + status registers. */ +#define NT_LOONGARCH_LSX 0xa02 /* LoongArch Loongson SIMD + Extension registers. */ +#define NT_LOONGARCH_LASX 0xa03 /* LoongArch Loongson Advanced + SIMD Extension registers. */ +#define NT_LOONGARCH_LBT 0xa04 /* LoongArch Loongson Binary + Translation registers. */ +#define NT_LOONGARCH_HW_BREAK 0xa05 /* LoongArch hardware breakpoint registers */ +#define NT_LOONGARCH_HW_WATCH 0xa06 /* LoongArch hardware watchpoint registers */ + +/* Legal values for the note segment descriptor types for object files. */ + +#define NT_VERSION 1 /* Contains a version string. */ + + +/* Dynamic section entry. */ + +typedef struct +{ + Elf32_Sword d_tag; /* Dynamic entry type */ + union + { + Elf32_Word d_val; /* Integer value */ + Elf32_Addr d_ptr; /* Address value */ + } d_un; +} Elf32_Dyn; + +typedef struct +{ + Elf64_Sxword d_tag; /* Dynamic entry type */ + union + { + Elf64_Xword d_val; /* Integer value */ + Elf64_Addr d_ptr; /* Address value */ + } d_un; +} Elf64_Dyn; + +/* Legal values for d_tag (dynamic entry type). */ + +#define DT_NULL 0 /* Marks end of dynamic section */ +#define DT_NEEDED 1 /* Name of needed library */ +#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */ +#define DT_PLTGOT 3 /* Processor defined value */ +#define DT_HASH 4 /* Address of symbol hash table */ +#define DT_STRTAB 5 /* Address of string table */ +#define DT_SYMTAB 6 /* Address of symbol table */ +#define DT_RELA 7 /* Address of Rela relocs */ +#define DT_RELASZ 8 /* Total size of Rela relocs */ +#define DT_RELAENT 9 /* Size of one Rela reloc */ +#define DT_STRSZ 10 /* Size of string table */ +#define DT_SYMENT 11 /* Size of one symbol table entry */ +#define DT_INIT 12 /* Address of init function */ +#define DT_FINI 13 /* Address of termination function */ +#define DT_SONAME 14 /* Name of shared object */ +#define DT_RPATH 15 /* Library search path (deprecated) */ +#define DT_SYMBOLIC 16 /* Start symbol search here */ +#define DT_REL 17 /* Address of Rel relocs */ +#define DT_RELSZ 18 /* Total size of Rel relocs */ +#define DT_RELENT 19 /* Size of one Rel reloc */ +#define DT_PLTREL 20 /* Type of reloc in PLT */ +#define DT_DEBUG 21 /* For debugging; unspecified */ +#define DT_TEXTREL 22 /* Reloc might modify .text */ +#define DT_JMPREL 23 /* Address of PLT relocs */ +#define DT_BIND_NOW 24 /* Process relocations of object */ +#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ +#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ +#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ +#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ +#define DT_RUNPATH 29 /* Library search path */ +#define DT_FLAGS 30 /* Flags for the object being loaded */ +#define DT_ENCODING 32 /* Start of encoded range */ +#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ +#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ +#define DT_SYMTAB_SHNDX 34 /* Address of SYMTAB_SHNDX section */ +#define DT_RELRSZ 35 /* Total size of RELR relative relocations */ +#define DT_RELR 36 /* Address of RELR relative relocations */ +#define DT_RELRENT 37 /* Size of one RELR relative relocaction */ +#define DT_NUM 38 /* Number used */ +#define DT_LOOS 0x6000000d /* Start of OS-specific */ +#define DT_HIOS 0x6ffff000 /* End of OS-specific */ +#define DT_LOPROC 0x70000000 /* Start of processor-specific */ +#define DT_HIPROC 0x7fffffff /* End of processor-specific */ +#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */ + +/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the + Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's + approach. */ +#define DT_VALRNGLO 0x6ffffd00 +#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */ +#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */ +#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */ +#define DT_CHECKSUM 0x6ffffdf8 +#define DT_PLTPADSZ 0x6ffffdf9 +#define DT_MOVEENT 0x6ffffdfa +#define DT_MOVESZ 0x6ffffdfb +#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */ +#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting + the following DT_* entry. */ +#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */ +#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */ +#define DT_VALRNGHI 0x6ffffdff +#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */ +#define DT_VALNUM 12 + +/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the + Dyn.d_un.d_ptr field of the Elf*_Dyn structure. + + If any adjustment is made to the ELF object after it has been + built these entries will need to be adjusted. */ +#define DT_ADDRRNGLO 0x6ffffe00 +#define DT_GNU_HASH 0x6ffffef5 /* GNU-style hash table. */ +#define DT_TLSDESC_PLT 0x6ffffef6 +#define DT_TLSDESC_GOT 0x6ffffef7 +#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */ +#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */ +#define DT_CONFIG 0x6ffffefa /* Configuration information. */ +#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */ +#define DT_AUDIT 0x6ffffefc /* Object auditing. */ +#define DT_PLTPAD 0x6ffffefd /* PLT padding. */ +#define DT_MOVETAB 0x6ffffefe /* Move table. */ +#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */ +#define DT_ADDRRNGHI 0x6ffffeff +#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */ +#define DT_ADDRNUM 11 + +/* The versioning entry types. The next are defined as part of the + GNU extension. */ +#define DT_VERSYM 0x6ffffff0 + +#define DT_RELACOUNT 0x6ffffff9 +#define DT_RELCOUNT 0x6ffffffa + +/* These were chosen by Sun. */ +#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */ +#define DT_VERDEF 0x6ffffffc /* Address of version definition + table */ +#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */ +#define DT_VERNEED 0x6ffffffe /* Address of table with needed + versions */ +#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */ +#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */ +#define DT_VERSIONTAGNUM 16 + +/* Sun added these machine-independent extensions in the "processor-specific" + range. Be compatible. */ +#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */ +#define DT_FILTER 0x7fffffff /* Shared object to get values from */ +#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) +#define DT_EXTRANUM 3 + +/* Values of `d_un.d_val' in the DT_FLAGS entry. */ +#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */ +#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */ +#define DF_TEXTREL 0x00000004 /* Object contains text relocations */ +#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */ +#define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */ + +/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1 + entry in the dynamic section. */ +#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */ +#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */ +#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */ +#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/ +#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/ +#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/ +#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */ +#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */ +#define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */ +#define DF_1_TRANS 0x00000200 +#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */ +#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */ +#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */ +#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/ +#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */ +#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */ +#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */ +#define DF_1_NODIRECT 0x00020000 /* Object has no-direct binding. */ +#define DF_1_IGNMULDEF 0x00040000 +#define DF_1_NOKSYMS 0x00080000 +#define DF_1_NOHDR 0x00100000 +#define DF_1_EDITED 0x00200000 /* Object is modified after built. */ +#define DF_1_NORELOC 0x00400000 +#define DF_1_SYMINTPOSE 0x00800000 /* Object has individual interposers. */ +#define DF_1_GLOBAUDIT 0x01000000 /* Global auditing required. */ +#define DF_1_SINGLETON 0x02000000 /* Singleton symbols are used. */ +#define DF_1_STUB 0x04000000 +#define DF_1_PIE 0x08000000 +#define DF_1_KMOD 0x10000000 +#define DF_1_WEAKFILTER 0x20000000 +#define DF_1_NOCOMMON 0x40000000 + +/* Flags for the feature selection in DT_FEATURE_1. */ +#define DTF_1_PARINIT 0x00000001 +#define DTF_1_CONFEXP 0x00000002 + +/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */ +#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */ +#define DF_P1_GROUPPERM 0x00000002 /* Symbols from next object are not + generally available. */ + +/* Version definition sections. */ + +typedef struct +{ + Elf32_Half vd_version; /* Version revision */ + Elf32_Half vd_flags; /* Version information */ + Elf32_Half vd_ndx; /* Version Index */ + Elf32_Half vd_cnt; /* Number of associated aux entries */ + Elf32_Word vd_hash; /* Version name hash value */ + Elf32_Word vd_aux; /* Offset in bytes to verdaux array */ + Elf32_Word vd_next; /* Offset in bytes to next verdef + entry */ +} Elf32_Verdef; + +typedef struct +{ + Elf64_Half vd_version; /* Version revision */ + Elf64_Half vd_flags; /* Version information */ + Elf64_Half vd_ndx; /* Version Index */ + Elf64_Half vd_cnt; /* Number of associated aux entries */ + Elf64_Word vd_hash; /* Version name hash value */ + Elf64_Word vd_aux; /* Offset in bytes to verdaux array */ + Elf64_Word vd_next; /* Offset in bytes to next verdef + entry */ +} Elf64_Verdef; + + +/* Legal values for vd_version (version revision). */ +#define VER_DEF_NONE 0 /* No version */ +#define VER_DEF_CURRENT 1 /* Current version */ +#define VER_DEF_NUM 2 /* Given version number */ + +/* Legal values for vd_flags (version information flags). */ +#define VER_FLG_BASE 0x1 /* Version definition of file itself */ +#define VER_FLG_WEAK 0x2 /* Weak version identifier. Also + used by vna_flags below. */ + +/* Versym symbol index values. */ +#define VER_NDX_LOCAL 0 /* Symbol is local. */ +#define VER_NDX_GLOBAL 1 /* Symbol is global. */ +#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */ +#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */ + +/* Auxiliary version information. */ + +typedef struct +{ + Elf32_Word vda_name; /* Version or dependency names */ + Elf32_Word vda_next; /* Offset in bytes to next verdaux + entry */ +} Elf32_Verdaux; + +typedef struct +{ + Elf64_Word vda_name; /* Version or dependency names */ + Elf64_Word vda_next; /* Offset in bytes to next verdaux + entry */ +} Elf64_Verdaux; + + +/* Version dependency section. */ + +typedef struct +{ + Elf32_Half vn_version; /* Version of structure */ + Elf32_Half vn_cnt; /* Number of associated aux entries */ + Elf32_Word vn_file; /* Offset of filename for this + dependency */ + Elf32_Word vn_aux; /* Offset in bytes to vernaux array */ + Elf32_Word vn_next; /* Offset in bytes to next verneed + entry */ +} Elf32_Verneed; + +typedef struct +{ + Elf64_Half vn_version; /* Version of structure */ + Elf64_Half vn_cnt; /* Number of associated aux entries */ + Elf64_Word vn_file; /* Offset of filename for this + dependency */ + Elf64_Word vn_aux; /* Offset in bytes to vernaux array */ + Elf64_Word vn_next; /* Offset in bytes to next verneed + entry */ +} Elf64_Verneed; + + +/* Legal values for vn_version (version revision). */ +#define VER_NEED_NONE 0 /* No version */ +#define VER_NEED_CURRENT 1 /* Current version */ +#define VER_NEED_NUM 2 /* Given version number */ + +/* Auxiliary needed version information. */ + +typedef struct +{ + Elf32_Word vna_hash; /* Hash value of dependency name */ + Elf32_Half vna_flags; /* Dependency specific information */ + Elf32_Half vna_other; /* Unused */ + Elf32_Word vna_name; /* Dependency name string offset */ + Elf32_Word vna_next; /* Offset in bytes to next vernaux + entry */ +} Elf32_Vernaux; + +typedef struct +{ + Elf64_Word vna_hash; /* Hash value of dependency name */ + Elf64_Half vna_flags; /* Dependency specific information */ + Elf64_Half vna_other; /* Unused */ + Elf64_Word vna_name; /* Dependency name string offset */ + Elf64_Word vna_next; /* Offset in bytes to next vernaux + entry */ +} Elf64_Vernaux; + + +/* Auxiliary vector. */ + +/* This vector is normally only used by the program interpreter. The + usual definition in an ABI supplement uses the name auxv_t. The + vector is not usually defined in a standard file, but it + can't hurt. We rename it to avoid conflicts. The sizes of these + types are an arrangement between the exec server and the program + interpreter, so we don't fully specify them here. */ + +typedef struct +{ + uint32_t a_type; /* Entry type */ + union + { + uint32_t a_val; /* Integer value */ + /* We use to have pointer elements added here. We cannot do that, + though, since it does not work when using 32-bit definitions + on 64-bit platforms and vice versa. */ + } a_un; +} Elf32_auxv_t; + +typedef struct +{ + uint64_t a_type; /* Entry type */ + union + { + uint64_t a_val; /* Integer value */ + /* We use to have pointer elements added here. We cannot do that, + though, since it does not work when using 32-bit definitions + on 64-bit platforms and vice versa. */ + } a_un; +} Elf64_auxv_t; + +/* Legal values for a_type (entry type). */ + +#define AT_NULL 0 /* End of vector */ +#define AT_IGNORE 1 /* Entry should be ignored */ +#define AT_EXECFD 2 /* File descriptor of program */ +#define AT_PHDR 3 /* Program headers for program */ +#define AT_PHENT 4 /* Size of program header entry */ +#define AT_PHNUM 5 /* Number of program headers */ +#define AT_PAGESZ 6 /* System page size */ +#define AT_BASE 7 /* Base address of interpreter */ +#define AT_FLAGS 8 /* Flags */ +#define AT_ENTRY 9 /* Entry point of program */ +#define AT_NOTELF 10 /* Program is not ELF */ +#define AT_UID 11 /* Real uid */ +#define AT_EUID 12 /* Effective uid */ +#define AT_GID 13 /* Real gid */ +#define AT_EGID 14 /* Effective gid */ +#define AT_CLKTCK 17 /* Frequency of times() */ + +/* Some more special a_type values describing the hardware. */ +#define AT_PLATFORM 15 /* String identifying platform. */ +#define AT_HWCAP 16 /* Machine-dependent hints about + processor capabilities. */ + +/* This entry gives some information about the FPU initialization + performed by the kernel. */ +#define AT_FPUCW 18 /* Used FPU control word. */ + +/* Cache block sizes. */ +#define AT_DCACHEBSIZE 19 /* Data cache block size. */ +#define AT_ICACHEBSIZE 20 /* Instruction cache block size. */ +#define AT_UCACHEBSIZE 21 /* Unified cache block size. */ + +/* A special ignored value for PPC, used by the kernel to control the + interpretation of the AUXV. Must be > 16. */ +#define AT_IGNOREPPC 22 /* Entry should be ignored. */ + +#define AT_SECURE 23 /* Boolean, was exec setuid-like? */ + +#define AT_BASE_PLATFORM 24 /* String identifying real platforms.*/ + +#define AT_RANDOM 25 /* Address of 16 random bytes. */ + +#define AT_HWCAP2 26 /* More machine-dependent hints about + processor capabilities. */ + +#define AT_RSEQ_FEATURE_SIZE 27 /* rseq supported feature size. */ +#define AT_RSEQ_ALIGN 28 /* rseq allocation alignment. */ + +/* More machine-dependent hints about processor capabilities. */ +#define AT_HWCAP3 29 /* extension of AT_HWCAP. */ +#define AT_HWCAP4 30 /* extension of AT_HWCAP. */ + +#define AT_EXECFN 31 /* Filename of executable. */ + +/* Pointer to the global system page used for system calls and other + nice things. */ +#define AT_SYSINFO 32 +#define AT_SYSINFO_EHDR 33 + +/* Shapes of the caches. Bits 0-3 contains associativity; bits 4-7 contains + log2 of line size; mask those to get cache size. */ +#define AT_L1I_CACHESHAPE 34 +#define AT_L1D_CACHESHAPE 35 +#define AT_L2_CACHESHAPE 36 +#define AT_L3_CACHESHAPE 37 + +/* Shapes of the caches, with more room to describe them. + *GEOMETRY are comprised of cache line size in bytes in the bottom 16 bits + and the cache associativity in the next 16 bits. */ +#define AT_L1I_CACHESIZE 40 +#define AT_L1I_CACHEGEOMETRY 41 +#define AT_L1D_CACHESIZE 42 +#define AT_L1D_CACHEGEOMETRY 43 +#define AT_L2_CACHESIZE 44 +#define AT_L2_CACHEGEOMETRY 45 +#define AT_L3_CACHESIZE 46 +#define AT_L3_CACHEGEOMETRY 47 + +#define AT_MINSIGSTKSZ 51 /* Stack needed for signal delivery */ + +/* Note section contents. Each entry in the note section begins with + a header of a fixed form. */ + +typedef struct +{ + Elf32_Word n_namesz; /* Length of the note's name. */ + Elf32_Word n_descsz; /* Length of the note's descriptor. */ + Elf32_Word n_type; /* Type of the note. */ +} Elf32_Nhdr; + +typedef struct +{ + Elf64_Word n_namesz; /* Length of the note's name. */ + Elf64_Word n_descsz; /* Length of the note's descriptor. */ + Elf64_Word n_type; /* Type of the note. */ +} Elf64_Nhdr; + +/* Known names of notes. */ + +/* Solaris entries in the note section have this name. */ +#define ELF_NOTE_SOLARIS "SUNW Solaris" + +/* Note entries for GNU systems have this name. */ +#define ELF_NOTE_GNU "GNU" + +/* Note entries for freedesktop.org have this name. */ +#define ELF_NOTE_FDO "FDO" + +/* Defined types of notes for Solaris. */ + +/* Value of descriptor (one word) is desired pagesize for the binary. */ +#define ELF_NOTE_PAGESIZE_HINT 1 + + +/* Defined note types for GNU systems. */ + +/* ABI information. The descriptor consists of words: + word 0: OS descriptor + word 1: major version of the ABI + word 2: minor version of the ABI + word 3: subminor version of the ABI +*/ +#define NT_GNU_ABI_TAG 1 +#define ELF_NOTE_ABI NT_GNU_ABI_TAG /* Old name. */ + +/* Known OSes. These values can appear in word 0 of an + NT_GNU_ABI_TAG note section entry. */ +#define ELF_NOTE_OS_LINUX 0 +#define ELF_NOTE_OS_GNU 1 +#define ELF_NOTE_OS_SOLARIS2 2 +#define ELF_NOTE_OS_FREEBSD 3 + +/* Synthetic hwcap information. The descriptor begins with two words: + word 0: number of entries + word 1: bitmask of enabled entries + Then follow variable-length entries, one byte followed by a + '\0'-terminated hwcap name string. The byte gives the bit + number to test if enabled, (1U << bit) & bitmask. */ +#define NT_GNU_HWCAP 2 + +/* Build ID bits as generated by ld --build-id. + The descriptor consists of any nonzero number of bytes. */ +#define NT_GNU_BUILD_ID 3 + +/* Version note generated by GNU gold containing a version string. */ +#define NT_GNU_GOLD_VERSION 4 + +/* Program property. */ +#define NT_GNU_PROPERTY_TYPE_0 5 + +/* Packaging metadata as defined on + https://systemd.io/ELF_PACKAGE_METADATA/ */ +#define NT_FDO_PACKAGING_METADATA 0xcafe1a7e + +/* dlopen metadata as defined on + https://systemd.io/ELF_DLOPEN_METADATA/ */ +#define NT_FDO_DLOPEN_METADATA 0x407c0c0a + +/* Note section name of program property. */ +#define NOTE_GNU_PROPERTY_SECTION_NAME ".note.gnu.property" + +/* Values used in GNU .note.gnu.property notes (NT_GNU_PROPERTY_TYPE_0). */ + +/* Stack size. */ +#define GNU_PROPERTY_STACK_SIZE 1 +/* No copy relocation on protected data symbol. */ +#define GNU_PROPERTY_NO_COPY_ON_PROTECTED 2 + +/* A 4-byte unsigned integer property: A bit is set if it is set in all + relocatable inputs. */ +#define GNU_PROPERTY_UINT32_AND_LO 0xb0000000 +#define GNU_PROPERTY_UINT32_AND_HI 0xb0007fff + +/* A 4-byte unsigned integer property: A bit is set if it is set in any + relocatable inputs. */ +#define GNU_PROPERTY_UINT32_OR_LO 0xb0008000 +#define GNU_PROPERTY_UINT32_OR_HI 0xb000ffff + +/* The needed properties by the object file. */ +#define GNU_PROPERTY_1_NEEDED GNU_PROPERTY_UINT32_OR_LO + +/* Set if the object file requires canonical function pointers and + cannot be used with copy relocation. */ +#define GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS (1U << 0) + +/* Processor-specific semantics, lo */ +#define GNU_PROPERTY_LOPROC 0xc0000000 +/* Processor-specific semantics, hi */ +#define GNU_PROPERTY_HIPROC 0xdfffffff +/* Application-specific semantics, lo */ +#define GNU_PROPERTY_LOUSER 0xe0000000 +/* Application-specific semantics, hi */ +#define GNU_PROPERTY_HIUSER 0xffffffff + +/* AArch64 specific GNU properties. */ +#define GNU_PROPERTY_AARCH64_FEATURE_1_AND 0xc0000000 + +#define GNU_PROPERTY_AARCH64_FEATURE_1_BTI (1U << 0) +#define GNU_PROPERTY_AARCH64_FEATURE_1_PAC (1U << 1) + +/* The x86 instruction sets indicated by the corresponding bits are + used in program. Their support in the hardware is optional. */ +#define GNU_PROPERTY_X86_ISA_1_USED 0xc0010002 +/* The x86 instruction sets indicated by the corresponding bits are + used in program and they must be supported by the hardware. */ +#define GNU_PROPERTY_X86_ISA_1_NEEDED 0xc0008002 +/* X86 processor-specific features used in program. */ +#define GNU_PROPERTY_X86_FEATURE_1_AND 0xc0000002 + +/* GNU_PROPERTY_X86_ISA_1_BASELINE: CMOV, CX8 (cmpxchg8b), FPU (fld), + MMX, OSFXSR (fxsave), SCE (syscall), SSE and SSE2. */ +#define GNU_PROPERTY_X86_ISA_1_BASELINE (1U << 0) +/* GNU_PROPERTY_X86_ISA_1_V2: GNU_PROPERTY_X86_ISA_1_BASELINE, + CMPXCHG16B (cmpxchg16b), LAHF-SAHF (lahf), POPCNT (popcnt), SSE3, + SSSE3, SSE4.1 and SSE4.2. */ +#define GNU_PROPERTY_X86_ISA_1_V2 (1U << 1) +/* GNU_PROPERTY_X86_ISA_1_V3: GNU_PROPERTY_X86_ISA_1_V2, AVX, AVX2, BMI1, + BMI2, F16C, FMA, LZCNT, MOVBE, XSAVE. */ +#define GNU_PROPERTY_X86_ISA_1_V3 (1U << 2) +/* GNU_PROPERTY_X86_ISA_1_V4: GNU_PROPERTY_X86_ISA_1_V3, AVX512F, + AVX512BW, AVX512CD, AVX512DQ and AVX512VL. */ +#define GNU_PROPERTY_X86_ISA_1_V4 (1U << 3) + +/* This indicates that all executable sections are compatible with + IBT. */ +#define GNU_PROPERTY_X86_FEATURE_1_IBT (1U << 0) +/* This indicates that all executable sections are compatible with + SHSTK. */ +#define GNU_PROPERTY_X86_FEATURE_1_SHSTK (1U << 1) + +/* Move records. */ +typedef struct +{ + Elf32_Xword m_value; /* Symbol value. */ + Elf32_Word m_info; /* Size and index. */ + Elf32_Word m_poffset; /* Symbol offset. */ + Elf32_Half m_repeat; /* Repeat count. */ + Elf32_Half m_stride; /* Stride info. */ +} Elf32_Move; + +typedef struct +{ + Elf64_Xword m_value; /* Symbol value. */ + Elf64_Xword m_info; /* Size and index. */ + Elf64_Xword m_poffset; /* Symbol offset. */ + Elf64_Half m_repeat; /* Repeat count. */ + Elf64_Half m_stride; /* Stride info. */ +} Elf64_Move; + +/* Macro to construct move records. */ +#define ELF32_M_SYM(info) ((info) >> 8) +#define ELF32_M_SIZE(info) ((unsigned char) (info)) +#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) + +#define ELF64_M_SYM(info) ELF32_M_SYM (info) +#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) +#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) + + +/* Motorola 68k specific definitions. */ + +/* Values for Elf32_Ehdr.e_flags. */ +#define EF_CPU32 0x00810000 + +/* m68k relocs. */ + +#define R_68K_NONE 0 /* No reloc */ +#define R_68K_32 1 /* Direct 32 bit */ +#define R_68K_16 2 /* Direct 16 bit */ +#define R_68K_8 3 /* Direct 8 bit */ +#define R_68K_PC32 4 /* PC relative 32 bit */ +#define R_68K_PC16 5 /* PC relative 16 bit */ +#define R_68K_PC8 6 /* PC relative 8 bit */ +#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */ +#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */ +#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */ +#define R_68K_GOT32O 10 /* 32 bit GOT offset */ +#define R_68K_GOT16O 11 /* 16 bit GOT offset */ +#define R_68K_GOT8O 12 /* 8 bit GOT offset */ +#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */ +#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */ +#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */ +#define R_68K_PLT32O 16 /* 32 bit PLT offset */ +#define R_68K_PLT16O 17 /* 16 bit PLT offset */ +#define R_68K_PLT8O 18 /* 8 bit PLT offset */ +#define R_68K_COPY 19 /* Copy symbol at runtime */ +#define R_68K_GLOB_DAT 20 /* Create GOT entry */ +#define R_68K_JMP_SLOT 21 /* Create PLT entry */ +#define R_68K_RELATIVE 22 /* Adjust by program base */ +#define R_68K_TLS_GD32 25 /* 32 bit GOT offset for GD */ +#define R_68K_TLS_GD16 26 /* 16 bit GOT offset for GD */ +#define R_68K_TLS_GD8 27 /* 8 bit GOT offset for GD */ +#define R_68K_TLS_LDM32 28 /* 32 bit GOT offset for LDM */ +#define R_68K_TLS_LDM16 29 /* 16 bit GOT offset for LDM */ +#define R_68K_TLS_LDM8 30 /* 8 bit GOT offset for LDM */ +#define R_68K_TLS_LDO32 31 /* 32 bit module-relative offset */ +#define R_68K_TLS_LDO16 32 /* 16 bit module-relative offset */ +#define R_68K_TLS_LDO8 33 /* 8 bit module-relative offset */ +#define R_68K_TLS_IE32 34 /* 32 bit GOT offset for IE */ +#define R_68K_TLS_IE16 35 /* 16 bit GOT offset for IE */ +#define R_68K_TLS_IE8 36 /* 8 bit GOT offset for IE */ +#define R_68K_TLS_LE32 37 /* 32 bit offset relative to + static TLS block */ +#define R_68K_TLS_LE16 38 /* 16 bit offset relative to + static TLS block */ +#define R_68K_TLS_LE8 39 /* 8 bit offset relative to + static TLS block */ +#define R_68K_TLS_DTPMOD32 40 /* 32 bit module number */ +#define R_68K_TLS_DTPREL32 41 /* 32 bit module-relative offset */ +#define R_68K_TLS_TPREL32 42 /* 32 bit TP-relative offset */ +/* Keep this the last entry. */ +#define R_68K_NUM 43 + +/* Intel 80386 specific definitions. */ + +/* i386 relocs. */ + +#define R_386_NONE 0 /* No reloc */ +#define R_386_32 1 /* Direct 32 bit */ +#define R_386_PC32 2 /* PC relative 32 bit */ +#define R_386_GOT32 3 /* 32 bit GOT entry */ +#define R_386_PLT32 4 /* 32 bit PLT address */ +#define R_386_COPY 5 /* Copy symbol at runtime */ +#define R_386_GLOB_DAT 6 /* Create GOT entry */ +#define R_386_JMP_SLOT 7 /* Create PLT entry */ +#define R_386_RELATIVE 8 /* Adjust by program base */ +#define R_386_GOTOFF 9 /* 32 bit offset to GOT */ +#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */ +#define R_386_32PLT 11 +#define R_386_TLS_TPOFF 14 /* Offset in static TLS block */ +#define R_386_TLS_IE 15 /* Address of GOT entry for static TLS + block offset */ +#define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block + offset */ +#define R_386_TLS_LE 17 /* Offset relative to static TLS + block */ +#define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of + general dynamic thread local data */ +#define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of + local dynamic thread local data + in LE code */ +#define R_386_16 20 +#define R_386_PC16 21 +#define R_386_8 22 +#define R_386_PC8 23 +#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic + thread local data */ +#define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */ +#define R_386_TLS_GD_CALL 26 /* Relocation for call to + __tls_get_addr() */ +#define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */ +#define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic + thread local data in LE code */ +#define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */ +#define R_386_TLS_LDM_CALL 30 /* Relocation for call to + __tls_get_addr() in LDM code */ +#define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */ +#define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */ +#define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS + block offset */ +#define R_386_TLS_LE_32 34 /* Negated offset relative to static + TLS block */ +#define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */ +#define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */ +#define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */ +#define R_386_SIZE32 38 /* 32-bit symbol size */ +#define R_386_TLS_GOTDESC 39 /* GOT offset for TLS descriptor. */ +#define R_386_TLS_DESC_CALL 40 /* Marker of call through TLS + descriptor for + relaxation. */ +#define R_386_TLS_DESC 41 /* TLS descriptor containing + pointer to code and to + argument, returning the TLS + offset for the symbol. */ +#define R_386_IRELATIVE 42 /* Adjust indirectly by program base */ +#define R_386_GOT32X 43 /* Load from 32 bit GOT entry, + relaxable. */ +/* Keep this the last entry. */ +#define R_386_NUM 44 + +/* SUN SPARC specific definitions. */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_SPARC_REGISTER 13 /* Global register reserved to app. */ + +/* Values for Elf64_Ehdr.e_flags. */ + +#define EF_SPARCV9_MM 3 +#define EF_SPARCV9_TSO 0 +#define EF_SPARCV9_PSO 1 +#define EF_SPARCV9_RMO 2 +#define EF_SPARC_LEDATA 0x800000 /* little endian data */ +#define EF_SPARC_EXT_MASK 0xFFFF00 +#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */ +#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */ +#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */ +#define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */ + +/* SPARC relocs. */ + +#define R_SPARC_NONE 0 /* No reloc */ +#define R_SPARC_8 1 /* Direct 8 bit */ +#define R_SPARC_16 2 /* Direct 16 bit */ +#define R_SPARC_32 3 /* Direct 32 bit */ +#define R_SPARC_DISP8 4 /* PC relative 8 bit */ +#define R_SPARC_DISP16 5 /* PC relative 16 bit */ +#define R_SPARC_DISP32 6 /* PC relative 32 bit */ +#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */ +#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */ +#define R_SPARC_HI22 9 /* High 22 bit */ +#define R_SPARC_22 10 /* Direct 22 bit */ +#define R_SPARC_13 11 /* Direct 13 bit */ +#define R_SPARC_LO10 12 /* Truncated 10 bit */ +#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */ +#define R_SPARC_GOT13 14 /* 13 bit GOT entry */ +#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */ +#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */ +#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */ +#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */ +#define R_SPARC_COPY 19 /* Copy symbol at runtime */ +#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */ +#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */ +#define R_SPARC_RELATIVE 22 /* Adjust by program base */ +#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */ + +/* Additional Sparc64 relocs. */ + +#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */ +#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */ +#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */ +#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */ +#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */ +#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */ +#define R_SPARC_10 30 /* Direct 10 bit */ +#define R_SPARC_11 31 /* Direct 11 bit */ +#define R_SPARC_64 32 /* Direct 64 bit */ +#define R_SPARC_OLO10 33 /* 10bit with secondary 13bit addend */ +#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */ +#define R_SPARC_HM10 35 /* High middle 10 bits of ... */ +#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */ +#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */ +#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */ +#define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */ +#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */ +#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */ +#define R_SPARC_GLOB_JMP 42 /* was part of v9 ABI but was removed */ +#define R_SPARC_7 43 /* Direct 7 bit */ +#define R_SPARC_5 44 /* Direct 5 bit */ +#define R_SPARC_6 45 /* Direct 6 bit */ +#define R_SPARC_DISP64 46 /* PC relative 64 bit */ +#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */ +#define R_SPARC_HIX22 48 /* High 22 bit complemented */ +#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */ +#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */ +#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */ +#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */ +#define R_SPARC_REGISTER 53 /* Global register usage */ +#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */ +#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */ +#define R_SPARC_TLS_GD_HI22 56 +#define R_SPARC_TLS_GD_LO10 57 +#define R_SPARC_TLS_GD_ADD 58 +#define R_SPARC_TLS_GD_CALL 59 +#define R_SPARC_TLS_LDM_HI22 60 +#define R_SPARC_TLS_LDM_LO10 61 +#define R_SPARC_TLS_LDM_ADD 62 +#define R_SPARC_TLS_LDM_CALL 63 +#define R_SPARC_TLS_LDO_HIX22 64 +#define R_SPARC_TLS_LDO_LOX10 65 +#define R_SPARC_TLS_LDO_ADD 66 +#define R_SPARC_TLS_IE_HI22 67 +#define R_SPARC_TLS_IE_LO10 68 +#define R_SPARC_TLS_IE_LD 69 +#define R_SPARC_TLS_IE_LDX 70 +#define R_SPARC_TLS_IE_ADD 71 +#define R_SPARC_TLS_LE_HIX22 72 +#define R_SPARC_TLS_LE_LOX10 73 +#define R_SPARC_TLS_DTPMOD32 74 +#define R_SPARC_TLS_DTPMOD64 75 +#define R_SPARC_TLS_DTPOFF32 76 +#define R_SPARC_TLS_DTPOFF64 77 +#define R_SPARC_TLS_TPOFF32 78 +#define R_SPARC_TLS_TPOFF64 79 +#define R_SPARC_GOTDATA_HIX22 80 +#define R_SPARC_GOTDATA_LOX10 81 +#define R_SPARC_GOTDATA_OP_HIX22 82 +#define R_SPARC_GOTDATA_OP_LOX10 83 +#define R_SPARC_GOTDATA_OP 84 +#define R_SPARC_H34 85 +#define R_SPARC_SIZE32 86 +#define R_SPARC_SIZE64 87 +#define R_SPARC_WDISP10 88 +#define R_SPARC_JMP_IREL 248 +#define R_SPARC_IRELATIVE 249 +#define R_SPARC_GNU_VTINHERIT 250 +#define R_SPARC_GNU_VTENTRY 251 +#define R_SPARC_REV32 252 +/* Keep this the last entry. */ +#define R_SPARC_NUM 253 + +/* For Sparc64, legal values for d_tag of Elf64_Dyn. */ + +#define DT_SPARC_REGISTER 0x70000001 +#define DT_SPARC_NUM 2 + +/* MIPS R3000 specific definitions. */ + +/* Legal values for e_flags field of Elf32_Ehdr. */ + +#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used. */ +#define EF_MIPS_PIC 2 /* Contains PIC code. */ +#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence. */ +#define EF_MIPS_XGOT 8 +#define EF_MIPS_UCODE 16 +#define EF_MIPS_ABI2 32 +#define EF_MIPS_ABI_ON32 64 +#define EF_MIPS_OPTIONS_FIRST 0x00000080 /* Process the .MIPS.options + section first by ld. */ +#define EF_MIPS_32BITMODE 0x00000100 /* Indicates code compiled for + a 64-bit machine in 32-bit + mode (regs are 32-bits + wide). */ +#define EF_MIPS_FP64 512 /* Uses FP64 (12 callee-saved). */ +#define EF_MIPS_NAN2008 1024 /* Uses IEEE 754-2008 NaN encoding. */ +#define EF_MIPS_ARCH_ASE 0x0f000000 /* Architectural Extensions + used by this file. */ +#define EF_MIPS_ARCH_ASE_MDMX 0x08000000 /* Use MDMX multimedia + extensions. */ +#define EF_MIPS_ARCH_ASE_M16 0x04000000 /* Use MIPS-16 ISA + extensions. */ +#define EF_MIPS_ARCH_ASE_MICROMIPS 0x02000000 /* Use MICROMIPS ISA + extensions. */ +#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level. */ + +/* Legal values for MIPS architecture level. */ + +#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ +#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ +#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ +#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ +#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ +#define EF_MIPS_ARCH_32 0x50000000 /* MIPS32 code. */ +#define EF_MIPS_ARCH_64 0x60000000 /* MIPS64 code. */ +#define EF_MIPS_ARCH_32R2 0x70000000 /* MIPS32r2 code. */ +#define EF_MIPS_ARCH_64R2 0x80000000 /* MIPS64r2 code. */ +#define EF_MIPS_ARCH_32R6 0x90000000 /* MIPS32r6 code. */ +#define EF_MIPS_ARCH_64R6 0xa0000000 /* MIPS64r6 code. */ +#define EF_MIPS_ABI 0x0000F000 /* The ABI of the file. Also + see EF_MIPS_ABI2 above. */ +#define EF_MIPS_ABI_O32 0x00001000 /* The original o32 abi. */ +#define EF_MIPS_ABI_O64 0x00002000 /* O32 extended to work on + 64 bit architectures. */ +#define EF_MIPS_ABI_EABI32 0x00003000 /* EABI in 32 bit mode. */ +#define EF_MIPS_ABI_EABI64 0x00004000 /* EABI in 64 bit mode. */ +#define EF_MIPS_MACH 0x00FF0000 +#define EF_MIPS_MACH_3900 0x00810000 +#define EF_MIPS_MACH_4010 0x00820000 +#define EF_MIPS_MACH_4100 0x00830000 +#define EF_MIPS_MACH_ALLEGREX 0x00840000 +#define EF_MIPS_MACH_4650 0x00850000 +#define EF_MIPS_MACH_4120 0x00870000 +#define EF_MIPS_MACH_4111 0x00880000 +#define EF_MIPS_MACH_SB1 0x008a0000 +#define EF_MIPS_MACH_OCTEON 0x008b0000 +#define EF_MIPS_MACH_XLR 0x008c0000 +#define EF_MIPS_MACH_OCTEON2 0x008d0000 +#define EF_MIPS_MACH_OCTEON3 0x008e0000 +#define EF_MIPS_MACH_5400 0x00910000 +#define EF_MIPS_MACH_5900 0x00920000 +#define EF_MIPS_MACH_IAMR2 0x00930000 +#define EF_MIPS_MACH_5500 0x00980000 +#define EF_MIPS_MACH_9000 0x00990000 +#define EF_MIPS_MACH_LS2E 0x00A00000 +#define EF_MIPS_MACH_LS2F 0x00A10000 +#define EF_MIPS_MACH_GS464 0x00A20000 +#define EF_MIPS_MACH_GS464E 0x00A30000 +#define EF_MIPS_MACH_GS264E 0x00A40000 + +/* The following are unofficial names and should not be used. */ + +#define E_MIPS_ARCH_1 EF_MIPS_ARCH_1 +#define E_MIPS_ARCH_2 EF_MIPS_ARCH_2 +#define E_MIPS_ARCH_3 EF_MIPS_ARCH_3 +#define E_MIPS_ARCH_4 EF_MIPS_ARCH_4 +#define E_MIPS_ARCH_5 EF_MIPS_ARCH_5 +#define E_MIPS_ARCH_32 EF_MIPS_ARCH_32 +#define E_MIPS_ARCH_64 EF_MIPS_ARCH_64 + +/* Special section indices. */ + +#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols. */ +#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */ +#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */ +#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols. */ +#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols. */ + +/* Legal values for sh_type field of Elf32_Shdr. */ + +#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link. */ +#define SHT_MIPS_MSYM 0x70000001 +#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols. */ +#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes. */ +#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */ +#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging info. */ +#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information. */ +#define SHT_MIPS_PACKAGE 0x70000007 +#define SHT_MIPS_PACKSYM 0x70000008 +#define SHT_MIPS_RELD 0x70000009 +#define SHT_MIPS_IFACE 0x7000000b +#define SHT_MIPS_CONTENT 0x7000000c +#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */ +#define SHT_MIPS_SHDR 0x70000010 +#define SHT_MIPS_FDESC 0x70000011 +#define SHT_MIPS_EXTSYM 0x70000012 +#define SHT_MIPS_DENSE 0x70000013 +#define SHT_MIPS_PDESC 0x70000014 +#define SHT_MIPS_LOCSYM 0x70000015 +#define SHT_MIPS_AUXSYM 0x70000016 +#define SHT_MIPS_OPTSYM 0x70000017 +#define SHT_MIPS_LOCSTR 0x70000018 +#define SHT_MIPS_LINE 0x70000019 +#define SHT_MIPS_RFDESC 0x7000001a +#define SHT_MIPS_DELTASYM 0x7000001b +#define SHT_MIPS_DELTAINST 0x7000001c +#define SHT_MIPS_DELTACLASS 0x7000001d +#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */ +#define SHT_MIPS_DELTADECL 0x7000001f +#define SHT_MIPS_SYMBOL_LIB 0x70000020 +#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */ +#define SHT_MIPS_TRANSLATE 0x70000022 +#define SHT_MIPS_PIXIE 0x70000023 +#define SHT_MIPS_XLATE 0x70000024 +#define SHT_MIPS_XLATE_DEBUG 0x70000025 +#define SHT_MIPS_WHIRL 0x70000026 +#define SHT_MIPS_EH_REGION 0x70000027 +#define SHT_MIPS_XLATE_OLD 0x70000028 +#define SHT_MIPS_PDR_EXCEPTION 0x70000029 +#define SHT_MIPS_ABIFLAGS 0x7000002a +#define SHT_MIPS_XHASH 0x7000002b + +/* Legal values for sh_flags field of Elf32_Shdr. */ + +#define SHF_MIPS_GPREL 0x10000000 /* Must be in global data area. */ +#define SHF_MIPS_MERGE 0x20000000 +#define SHF_MIPS_ADDR 0x40000000 +#define SHF_MIPS_STRINGS 0x80000000 +#define SHF_MIPS_NOSTRIP 0x08000000 +#define SHF_MIPS_LOCAL 0x04000000 +#define SHF_MIPS_NAMES 0x02000000 +#define SHF_MIPS_NODUPE 0x01000000 + + +/* Symbol tables. */ + +/* MIPS specific values for `st_other'. */ +#define STO_MIPS_DEFAULT 0x0 +#define STO_MIPS_INTERNAL 0x1 +#define STO_MIPS_HIDDEN 0x2 +#define STO_MIPS_PROTECTED 0x3 +#define STO_MIPS_PLT 0x8 +#define STO_MIPS_SC_ALIGN_UNUSED 0xff + +/* MIPS specific values for `st_info'. */ +#define STB_MIPS_SPLIT_COMMON 13 + +/* Entries found in sections of type SHT_MIPS_GPTAB. */ + +typedef union +{ + struct + { + Elf32_Word gt_current_g_value; /* -G value used for compilation. */ + Elf32_Word gt_unused; /* Not used. */ + } gt_header; /* First entry in section. */ + struct + { + Elf32_Word gt_g_value; /* If this value were used for -G. */ + Elf32_Word gt_bytes; /* This many bytes would be used. */ + } gt_entry; /* Subsequent entries in section. */ +} Elf32_gptab; + +/* Entry found in sections of type SHT_MIPS_REGINFO. */ + +typedef struct +{ + Elf32_Word ri_gprmask; /* General registers used. */ + Elf32_Word ri_cprmask[4]; /* Coprocessor registers used. */ + Elf32_Sword ri_gp_value; /* $gp register value. */ +} Elf32_RegInfo; + +/* Entries found in sections of type SHT_MIPS_OPTIONS. */ + +typedef struct +{ + unsigned char kind; /* Determines interpretation of the + variable part of descriptor. */ + unsigned char size; /* Size of descriptor, including header. */ + Elf32_Section section; /* Section header index of section affected, + 0 for global options. */ + Elf32_Word info; /* Kind-specific information. */ +} Elf_Options; + +/* Values for `kind' field in Elf_Options. */ + +#define ODK_NULL 0 /* Undefined. */ +#define ODK_REGINFO 1 /* Register usage information. */ +#define ODK_EXCEPTIONS 2 /* Exception processing options. */ +#define ODK_PAD 3 /* Section padding options. */ +#define ODK_HWPATCH 4 /* Hardware workarounds performed */ +#define ODK_FILL 5 /* record the fill value used by the linker. */ +#define ODK_TAGS 6 /* reserve space for desktop tools to write. */ +#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */ +#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */ + +/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */ + +#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */ +#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */ +#define OEX_PAGE0 0x10000 /* page zero must be mapped. */ +#define OEX_SMM 0x20000 /* Force sequential memory mode? */ +#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */ +#define OEX_PRECISEFP OEX_FPDBUG +#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */ + +#define OEX_FPU_INVAL 0x10 +#define OEX_FPU_DIV0 0x08 +#define OEX_FPU_OFLO 0x04 +#define OEX_FPU_UFLO 0x02 +#define OEX_FPU_INEX 0x01 + +/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */ + +#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */ +#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */ +#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */ +#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */ + +#define OPAD_PREFIX 0x1 +#define OPAD_POSTFIX 0x2 +#define OPAD_SYMBOL 0x4 + +/* Entry found in `.options' section. */ + +typedef struct +{ + Elf32_Word hwp_flags1; /* Extra flags. */ + Elf32_Word hwp_flags2; /* Extra flags. */ +} Elf_Options_Hw; + +/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */ + +#define OHWA0_R4KEOP_CHECKED 0x00000001 +#define OHWA1_R4KEOP_CLEAN 0x00000002 + +/* MIPS relocs. */ + +#define R_MIPS_NONE 0 /* No reloc */ +#define R_MIPS_16 1 /* Direct 16 bit */ +#define R_MIPS_32 2 /* Direct 32 bit */ +#define R_MIPS_REL32 3 /* PC relative 32 bit */ +#define R_MIPS_26 4 /* Direct 26 bit shifted */ +#define R_MIPS_HI16 5 /* High 16 bit */ +#define R_MIPS_LO16 6 /* Low 16 bit */ +#define R_MIPS_GPREL16 7 /* GP relative 16 bit */ +#define R_MIPS_LITERAL 8 /* 16 bit literal entry */ +#define R_MIPS_GOT16 9 /* 16 bit GOT entry */ +#define R_MIPS_PC16 10 /* PC relative 16 bit */ +#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */ +#define R_MIPS_GPREL32 12 /* GP relative 32 bit */ + +#define R_MIPS_SHIFT5 16 +#define R_MIPS_SHIFT6 17 +#define R_MIPS_64 18 +#define R_MIPS_GOT_DISP 19 +#define R_MIPS_GOT_PAGE 20 +#define R_MIPS_GOT_OFST 21 +#define R_MIPS_GOT_HI16 22 +#define R_MIPS_GOT_LO16 23 +#define R_MIPS_SUB 24 +#define R_MIPS_INSERT_A 25 +#define R_MIPS_INSERT_B 26 +#define R_MIPS_DELETE 27 +#define R_MIPS_HIGHER 28 +#define R_MIPS_HIGHEST 29 +#define R_MIPS_CALL_HI16 30 +#define R_MIPS_CALL_LO16 31 +#define R_MIPS_SCN_DISP 32 +#define R_MIPS_REL16 33 +#define R_MIPS_ADD_IMMEDIATE 34 +#define R_MIPS_PJUMP 35 +#define R_MIPS_RELGOT 36 +#define R_MIPS_JALR 37 +#define R_MIPS_TLS_DTPMOD32 38 /* Module number 32 bit */ +#define R_MIPS_TLS_DTPREL32 39 /* Module-relative offset 32 bit */ +#define R_MIPS_TLS_DTPMOD64 40 /* Module number 64 bit */ +#define R_MIPS_TLS_DTPREL64 41 /* Module-relative offset 64 bit */ +#define R_MIPS_TLS_GD 42 /* 16 bit GOT offset for GD */ +#define R_MIPS_TLS_LDM 43 /* 16 bit GOT offset for LDM */ +#define R_MIPS_TLS_DTPREL_HI16 44 /* Module-relative offset, high 16 bits */ +#define R_MIPS_TLS_DTPREL_LO16 45 /* Module-relative offset, low 16 bits */ +#define R_MIPS_TLS_GOTTPREL 46 /* 16 bit GOT offset for IE */ +#define R_MIPS_TLS_TPREL32 47 /* TP-relative offset, 32 bit */ +#define R_MIPS_TLS_TPREL64 48 /* TP-relative offset, 64 bit */ +#define R_MIPS_TLS_TPREL_HI16 49 /* TP-relative offset, high 16 bits */ +#define R_MIPS_TLS_TPREL_LO16 50 /* TP-relative offset, low 16 bits */ +#define R_MIPS_GLOB_DAT 51 +#define R_MIPS_PC21_S2 60 +#define R_MIPS_PC26_S2 61 +#define R_MIPS_PC18_S3 62 +#define R_MIPS_PC19_S2 63 +#define R_MIPS_PCHI16 64 +#define R_MIPS_PCLO16 65 +#define R_MIPS16_26 100 +#define R_MIPS16_GPREL 101 +#define R_MIPS16_GOT16 102 +#define R_MIPS16_CALL16 103 +#define R_MIPS16_HI16 104 +#define R_MIPS16_LO16 105 +#define R_MIPS16_TLS_GD 106 +#define R_MIPS16_TLS_LDM 107 +#define R_MIPS16_TLS_DTPREL_HI16 108 +#define R_MIPS16_TLS_DTPREL_LO16 109 +#define R_MIPS16_TLS_GOTTPREL 110 +#define R_MIPS16_TLS_TPREL_HI16 111 +#define R_MIPS16_TLS_TPREL_LO16 112 +#define R_MIPS16_PC16_S1 113 +#define R_MIPS_COPY 126 +#define R_MIPS_JUMP_SLOT 127 +#define R_MIPS_RELATIVE 128 +#define R_MICROMIPS_26_S1 133 +#define R_MICROMIPS_HI16 134 +#define R_MICROMIPS_LO16 135 +#define R_MICROMIPS_GPREL16 136 +#define R_MICROMIPS_LITERAL 137 +#define R_MICROMIPS_GOT16 138 +#define R_MICROMIPS_PC7_S1 139 +#define R_MICROMIPS_PC10_S1 140 +#define R_MICROMIPS_PC16_S1 141 +#define R_MICROMIPS_CALL16 142 +#define R_MICROMIPS_GOT_DISP 145 +#define R_MICROMIPS_GOT_PAGE 146 +#define R_MICROMIPS_GOT_OFST 147 +#define R_MICROMIPS_GOT_HI16 148 +#define R_MICROMIPS_GOT_LO16 149 +#define R_MICROMIPS_SUB 150 +#define R_MICROMIPS_HIGHER 151 +#define R_MICROMIPS_HIGHEST 152 +#define R_MICROMIPS_CALL_HI16 153 +#define R_MICROMIPS_CALL_LO16 154 +#define R_MICROMIPS_SCN_DISP 155 +#define R_MICROMIPS_JALR 156 +#define R_MICROMIPS_HI0_LO16 157 +#define R_MICROMIPS_TLS_GD 162 +#define R_MICROMIPS_TLS_LDM 163 +#define R_MICROMIPS_TLS_DTPREL_HI16 164 +#define R_MICROMIPS_TLS_DTPREL_LO16 165 +#define R_MICROMIPS_TLS_GOTTPREL 166 +#define R_MICROMIPS_TLS_TPREL_HI16 169 +#define R_MICROMIPS_TLS_TPREL_LO16 170 +#define R_MICROMIPS_GPREL7_S2 172 +#define R_MICROMIPS_PC23_S2 173 +#define R_MIPS_PC32 248 +#define R_MIPS_EH 249 +#define R_MIPS_GNU_REL16_S2 250 +#define R_MIPS_GNU_VTINHERIT 253 +#define R_MIPS_GNU_VTENTRY 254 +/* Keep this the last entry. */ +#define R_MIPS_NUM 255 + +/* Legal values for p_type field of Elf32_Phdr. */ + +#define PT_MIPS_REGINFO 0x70000000 /* Register usage information. */ +#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */ +#define PT_MIPS_OPTIONS 0x70000002 +#define PT_MIPS_ABIFLAGS 0x70000003 /* FP mode requirement. */ + +/* Special program header types. */ + +#define PF_MIPS_LOCAL 0x10000000 + +/* Legal values for d_tag field of Elf32_Dyn. */ + +#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */ +#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */ +#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */ +#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */ +#define DT_MIPS_FLAGS 0x70000005 /* Flags */ +#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */ +#define DT_MIPS_MSYM 0x70000007 +#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */ +#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */ +#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */ +#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */ +#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */ +#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */ +#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */ +#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */ +#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */ +#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */ +#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */ +#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in + DT_MIPS_DELTA_CLASS. */ +#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */ +#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in + DT_MIPS_DELTA_INSTANCE. */ +#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */ +#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in + DT_MIPS_DELTA_RELOC. */ +#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta + relocations refer to. */ +#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in + DT_MIPS_DELTA_SYM. */ +#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the + class declaration. */ +#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in + DT_MIPS_DELTA_CLASSSYM. */ +#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */ +#define DT_MIPS_PIXIE_INIT 0x70000023 +#define DT_MIPS_SYMBOL_LIB 0x70000024 +#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 +#define DT_MIPS_LOCAL_GOTIDX 0x70000026 +#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 +#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 +#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */ +#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */ +#define DT_MIPS_DYNSTR_ALIGN 0x7000002b +#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */ +#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve + function stored in GOT. */ +#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added + by rld on dlopen() calls. */ +#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */ +#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */ +#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */ +/* The address of .got.plt in an executable using the new non-PIC ABI. */ +#define DT_MIPS_PLTGOT 0x70000032 +/* The base of the PLT in an executable using the new non-PIC ABI if that + PLT is writable. For a non-writable PLT, this is omitted or has a zero + value. */ +#define DT_MIPS_RWPLT 0x70000034 +/* An alternative description of the classic MIPS RLD_MAP that is usable + in a PIE as it stores a relative offset from the address of the tag + rather than an absolute address. */ +#define DT_MIPS_RLD_MAP_REL 0x70000035 +/* GNU-style hash table with xlat. */ +#define DT_MIPS_XHASH 0x70000036 +#define DT_MIPS_NUM 0x37 + +/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */ + +#define RHF_NONE 0 /* No flags */ +#define RHF_QUICKSTART (1 << 0) /* Use quickstart */ +#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */ +#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */ +#define RHF_NO_MOVE (1 << 3) +#define RHF_SGI_ONLY (1 << 4) +#define RHF_GUARANTEE_INIT (1 << 5) +#define RHF_DELTA_C_PLUS_PLUS (1 << 6) +#define RHF_GUARANTEE_START_INIT (1 << 7) +#define RHF_PIXIE (1 << 8) +#define RHF_DEFAULT_DELAY_LOAD (1 << 9) +#define RHF_REQUICKSTART (1 << 10) +#define RHF_REQUICKSTARTED (1 << 11) +#define RHF_CORD (1 << 12) +#define RHF_NO_UNRES_UNDEF (1 << 13) +#define RHF_RLD_ORDER_SAFE (1 << 14) + +/* Entries found in sections of type SHT_MIPS_LIBLIST. */ + +typedef struct +{ + Elf32_Word l_name; /* Name (string table index) */ + Elf32_Word l_time_stamp; /* Timestamp */ + Elf32_Word l_checksum; /* Checksum */ + Elf32_Word l_version; /* Interface version */ + Elf32_Word l_flags; /* Flags */ +} Elf32_Lib; + +typedef struct +{ + Elf64_Word l_name; /* Name (string table index) */ + Elf64_Word l_time_stamp; /* Timestamp */ + Elf64_Word l_checksum; /* Checksum */ + Elf64_Word l_version; /* Interface version */ + Elf64_Word l_flags; /* Flags */ +} Elf64_Lib; + + +/* Legal values for l_flags. */ + +#define LL_NONE 0 +#define LL_EXACT_MATCH (1 << 0) /* Require exact match */ +#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */ +#define LL_REQUIRE_MINOR (1 << 2) +#define LL_EXPORTS (1 << 3) +#define LL_DELAY_LOAD (1 << 4) +#define LL_DELTA (1 << 5) + +/* Entries found in sections of type SHT_MIPS_CONFLICT. */ + +typedef Elf32_Addr Elf32_Conflict; + +typedef struct +{ + /* Version of flags structure. */ + Elf32_Half version; + /* The level of the ISA: 1-5, 32, 64. */ + unsigned char isa_level; + /* The revision of ISA: 0 for MIPS V and below, 1-n otherwise. */ + unsigned char isa_rev; + /* The size of general purpose registers. */ + unsigned char gpr_size; + /* The size of co-processor 1 registers. */ + unsigned char cpr1_size; + /* The size of co-processor 2 registers. */ + unsigned char cpr2_size; + /* The floating-point ABI. */ + unsigned char fp_abi; + /* Processor-specific extension. */ + Elf32_Word isa_ext; + /* Mask of ASEs used. */ + Elf32_Word ases; + /* Mask of general flags. */ + Elf32_Word flags1; + Elf32_Word flags2; +} Elf_MIPS_ABIFlags_v0; + +/* Values for the register size bytes of an abi flags structure. */ + +#define MIPS_AFL_REG_NONE 0x00 /* No registers. */ +#define MIPS_AFL_REG_32 0x01 /* 32-bit registers. */ +#define MIPS_AFL_REG_64 0x02 /* 64-bit registers. */ +#define MIPS_AFL_REG_128 0x03 /* 128-bit registers. */ + +/* Masks for the ases word of an ABI flags structure. */ + +#define MIPS_AFL_ASE_DSP 0x00000001 /* DSP ASE. */ +#define MIPS_AFL_ASE_DSPR2 0x00000002 /* DSP R2 ASE. */ +#define MIPS_AFL_ASE_EVA 0x00000004 /* Enhanced VA Scheme. */ +#define MIPS_AFL_ASE_MCU 0x00000008 /* MCU (MicroController) ASE. */ +#define MIPS_AFL_ASE_MDMX 0x00000010 /* MDMX ASE. */ +#define MIPS_AFL_ASE_MIPS3D 0x00000020 /* MIPS-3D ASE. */ +#define MIPS_AFL_ASE_MT 0x00000040 /* MT ASE. */ +#define MIPS_AFL_ASE_SMARTMIPS 0x00000080 /* SmartMIPS ASE. */ +#define MIPS_AFL_ASE_VIRT 0x00000100 /* VZ ASE. */ +#define MIPS_AFL_ASE_MSA 0x00000200 /* MSA ASE. */ +#define MIPS_AFL_ASE_MIPS16 0x00000400 /* MIPS16 ASE. */ +#define MIPS_AFL_ASE_MICROMIPS 0x00000800 /* MICROMIPS ASE. */ +#define MIPS_AFL_ASE_XPA 0x00001000 /* XPA ASE. */ +#define MIPS_AFL_ASE_MASK 0x00001fff /* All ASEs. */ + +/* Values for the isa_ext word of an ABI flags structure. */ + +#define MIPS_AFL_EXT_XLR 1 /* RMI Xlr instruction. */ +#define MIPS_AFL_EXT_OCTEON2 2 /* Cavium Networks Octeon2. */ +#define MIPS_AFL_EXT_OCTEONP 3 /* Cavium Networks OcteonP. */ +#define MIPS_AFL_EXT_LOONGSON_3A 4 /* Loongson 3A. */ +#define MIPS_AFL_EXT_OCTEON 5 /* Cavium Networks Octeon. */ +#define MIPS_AFL_EXT_5900 6 /* MIPS R5900 instruction. */ +#define MIPS_AFL_EXT_4650 7 /* MIPS R4650 instruction. */ +#define MIPS_AFL_EXT_4010 8 /* LSI R4010 instruction. */ +#define MIPS_AFL_EXT_4100 9 /* NEC VR4100 instruction. */ +#define MIPS_AFL_EXT_3900 10 /* Toshiba R3900 instruction. */ +#define MIPS_AFL_EXT_10000 11 /* MIPS R10000 instruction. */ +#define MIPS_AFL_EXT_SB1 12 /* Broadcom SB-1 instruction. */ +#define MIPS_AFL_EXT_4111 13 /* NEC VR4111/VR4181 instruction. */ +#define MIPS_AFL_EXT_4120 14 /* NEC VR4120 instruction. */ +#define MIPS_AFL_EXT_5400 15 /* NEC VR5400 instruction. */ +#define MIPS_AFL_EXT_5500 16 /* NEC VR5500 instruction. */ +#define MIPS_AFL_EXT_LOONGSON_2E 17 /* ST Microelectronics Loongson 2E. */ +#define MIPS_AFL_EXT_LOONGSON_2F 18 /* ST Microelectronics Loongson 2F. */ + +/* Masks for the flags1 word of an ABI flags structure. */ +#define MIPS_AFL_FLAGS1_ODDSPREG 1 /* Uses odd single-precision registers. */ + +/* Object attribute values. */ +enum +{ + /* Not tagged or not using any ABIs affected by the differences. */ + Val_GNU_MIPS_ABI_FP_ANY = 0, + /* Using hard-float -mdouble-float. */ + Val_GNU_MIPS_ABI_FP_DOUBLE = 1, + /* Using hard-float -msingle-float. */ + Val_GNU_MIPS_ABI_FP_SINGLE = 2, + /* Using soft-float. */ + Val_GNU_MIPS_ABI_FP_SOFT = 3, + /* Using -mips32r2 -mfp64. */ + Val_GNU_MIPS_ABI_FP_OLD_64 = 4, + /* Using -mfpxx. */ + Val_GNU_MIPS_ABI_FP_XX = 5, + /* Using -mips32r2 -mfp64. */ + Val_GNU_MIPS_ABI_FP_64 = 6, + /* Using -mips32r2 -mfp64 -mno-odd-spreg. */ + Val_GNU_MIPS_ABI_FP_64A = 7, + /* Maximum allocated FP ABI value. */ + Val_GNU_MIPS_ABI_FP_MAX = 7 +}; + +/* HPPA specific definitions. */ + +/* Legal values for e_flags field of Elf32_Ehdr. */ + +#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */ +#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */ +#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */ +#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */ +#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch + prediction. */ +#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */ +#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */ + +/* Defined values for `e_flags & EF_PARISC_ARCH' are: */ + +#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */ +#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */ +#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */ + +/* Additional section indices. */ + +#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tentatively declared + symbols in ANSI C. */ +#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */ + +/* Legal values for sh_type field of Elf32_Shdr. */ + +#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */ +#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */ +#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */ + +/* Legal values for sh_flags field of Elf32_Shdr. */ + +#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ +#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */ +#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ + +#define STT_HP_OPAQUE (STT_LOOS + 0x1) +#define STT_HP_STUB (STT_LOOS + 0x2) + +/* HPPA relocs. */ + +#define R_PARISC_NONE 0 /* No reloc. */ +#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */ +#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */ +#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */ +#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */ +#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */ +#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */ +#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */ +#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */ +#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */ +#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */ +#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */ +#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */ +#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */ +#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */ +#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */ +#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */ +#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */ +#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */ +#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */ +#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */ +#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */ +#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */ +#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */ +#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */ +#define R_PARISC_FPTR64 64 /* 64 bits function address. */ +#define R_PARISC_PLABEL32 65 /* 32 bits function address. */ +#define R_PARISC_PLABEL21L 66 /* Left 21 bits of fdesc address. */ +#define R_PARISC_PLABEL14R 70 /* Right 14 bits of fdesc address. */ +#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */ +#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */ +#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */ +#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */ +#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */ +#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */ +#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */ +#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */ +#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */ +#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */ +#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */ +#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */ +#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */ +#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */ +#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */ +#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */ +#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */ +#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */ +#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */ +#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */ +#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */ +#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */ +#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */ +#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */ +#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */ +#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */ +#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */ +#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */ +#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */ +#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */ +#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */ +#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */ +#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LORESERVE 128 +#define R_PARISC_COPY 128 /* Copy relocation. */ +#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */ +#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */ +#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */ +#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */ +#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */ +#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */ +#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */ +#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */ +#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */ +#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */ +#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_GNU_VTENTRY 232 +#define R_PARISC_GNU_VTINHERIT 233 +#define R_PARISC_TLS_GD21L 234 /* GD 21-bit left. */ +#define R_PARISC_TLS_GD14R 235 /* GD 14-bit right. */ +#define R_PARISC_TLS_GDCALL 236 /* GD call to __t_g_a. */ +#define R_PARISC_TLS_LDM21L 237 /* LD module 21-bit left. */ +#define R_PARISC_TLS_LDM14R 238 /* LD module 14-bit right. */ +#define R_PARISC_TLS_LDMCALL 239 /* LD module call to __t_g_a. */ +#define R_PARISC_TLS_LDO21L 240 /* LD offset 21-bit left. */ +#define R_PARISC_TLS_LDO14R 241 /* LD offset 14-bit right. */ +#define R_PARISC_TLS_DTPMOD32 242 /* DTP module 32-bit. */ +#define R_PARISC_TLS_DTPMOD64 243 /* DTP module 64-bit. */ +#define R_PARISC_TLS_DTPOFF32 244 /* DTP offset 32-bit. */ +#define R_PARISC_TLS_DTPOFF64 245 /* DTP offset 32-bit. */ +#define R_PARISC_TLS_LE21L R_PARISC_TPREL21L +#define R_PARISC_TLS_LE14R R_PARISC_TPREL14R +#define R_PARISC_TLS_IE21L R_PARISC_LTOFF_TP21L +#define R_PARISC_TLS_IE14R R_PARISC_LTOFF_TP14R +#define R_PARISC_TLS_TPREL32 R_PARISC_TPREL32 +#define R_PARISC_TLS_TPREL64 R_PARISC_TPREL64 +#define R_PARISC_HIRESERVE 255 + +/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */ + +#define PT_HP_TLS (PT_LOOS + 0x0) +#define PT_HP_CORE_NONE (PT_LOOS + 0x1) +#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) +#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) +#define PT_HP_CORE_COMM (PT_LOOS + 0x4) +#define PT_HP_CORE_PROC (PT_LOOS + 0x5) +#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) +#define PT_HP_CORE_STACK (PT_LOOS + 0x7) +#define PT_HP_CORE_SHM (PT_LOOS + 0x8) +#define PT_HP_CORE_MMF (PT_LOOS + 0x9) +#define PT_HP_PARALLEL (PT_LOOS + 0x10) +#define PT_HP_FASTBIND (PT_LOOS + 0x11) +#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) +#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) +#define PT_HP_STACK (PT_LOOS + 0x14) + +#define PT_PARISC_ARCHEXT 0x70000000 +#define PT_PARISC_UNWIND 0x70000001 + +/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */ + +#define PF_PARISC_SBP 0x08000000 + +#define PF_HP_PAGE_SIZE 0x00100000 +#define PF_HP_FAR_SHARED 0x00200000 +#define PF_HP_NEAR_SHARED 0x00400000 +#define PF_HP_CODE 0x01000000 +#define PF_HP_MODIFY 0x02000000 +#define PF_HP_LAZYSWAP 0x04000000 +#define PF_HP_SBP 0x08000000 + + +/* Alpha specific definitions. */ + +/* Legal values for e_flags field of Elf64_Ehdr. */ + +#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */ +#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */ + +/* Legal values for sh_type field of Elf64_Shdr. */ + +/* These two are primerily concerned with ECOFF debugging info. */ +#define SHT_ALPHA_DEBUG 0x70000001 +#define SHT_ALPHA_REGINFO 0x70000002 + +/* Legal values for sh_flags field of Elf64_Shdr. */ + +#define SHF_ALPHA_GPREL 0x10000000 + +/* Legal values for st_other field of Elf64_Sym. */ +#define STO_ALPHA_NOPV 0x80 /* No PV required. */ +#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */ + +/* Alpha relocs. */ + +#define R_ALPHA_NONE 0 /* No reloc */ +#define R_ALPHA_REFLONG 1 /* Direct 32 bit */ +#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */ +#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */ +#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */ +#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */ +#define R_ALPHA_GPDISP 6 /* Add displacement to GP */ +#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */ +#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */ +#define R_ALPHA_SREL16 9 /* PC relative 16 bit */ +#define R_ALPHA_SREL32 10 /* PC relative 32 bit */ +#define R_ALPHA_SREL64 11 /* PC relative 64 bit */ +#define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */ +#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */ +#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */ +#define R_ALPHA_COPY 24 /* Copy symbol at runtime */ +#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */ +#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */ +#define R_ALPHA_RELATIVE 27 /* Adjust by program base */ +#define R_ALPHA_TLS_GD_HI 28 +#define R_ALPHA_TLSGD 29 +#define R_ALPHA_TLS_LDM 30 +#define R_ALPHA_DTPMOD64 31 +#define R_ALPHA_GOTDTPREL 32 +#define R_ALPHA_DTPREL64 33 +#define R_ALPHA_DTPRELHI 34 +#define R_ALPHA_DTPRELLO 35 +#define R_ALPHA_DTPREL16 36 +#define R_ALPHA_GOTTPREL 37 +#define R_ALPHA_TPREL64 38 +#define R_ALPHA_TPRELHI 39 +#define R_ALPHA_TPRELLO 40 +#define R_ALPHA_TPREL16 41 +/* Keep this the last entry. */ +#define R_ALPHA_NUM 46 + +/* Magic values of the LITUSE relocation addend. */ +#define LITUSE_ALPHA_ADDR 0 +#define LITUSE_ALPHA_BASE 1 +#define LITUSE_ALPHA_BYTOFF 2 +#define LITUSE_ALPHA_JSR 3 +#define LITUSE_ALPHA_TLS_GD 4 +#define LITUSE_ALPHA_TLS_LDM 5 + +/* Legal values for d_tag of Elf64_Dyn. */ +#define DT_ALPHA_PLTRO (DT_LOPROC + 0) +#define DT_ALPHA_NUM 1 + +/* PowerPC specific declarations */ + +/* Values for Elf32/64_Ehdr.e_flags. */ +#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ + +/* Cygnus local bits below */ +#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ +#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib + flag */ + +/* PowerPC relocations defined by the ABIs */ +#define R_PPC_NONE 0 +#define R_PPC_ADDR32 1 /* 32bit absolute address */ +#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ +#define R_PPC_ADDR16 3 /* 16bit absolute address */ +#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ +#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ +#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ +#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ +#define R_PPC_ADDR14_BRTAKEN 8 +#define R_PPC_ADDR14_BRNTAKEN 9 +#define R_PPC_REL24 10 /* PC relative 26 bit */ +#define R_PPC_REL14 11 /* PC relative 16 bit */ +#define R_PPC_REL14_BRTAKEN 12 +#define R_PPC_REL14_BRNTAKEN 13 +#define R_PPC_GOT16 14 +#define R_PPC_GOT16_LO 15 +#define R_PPC_GOT16_HI 16 +#define R_PPC_GOT16_HA 17 +#define R_PPC_PLTREL24 18 +#define R_PPC_COPY 19 +#define R_PPC_GLOB_DAT 20 +#define R_PPC_JMP_SLOT 21 +#define R_PPC_RELATIVE 22 +#define R_PPC_LOCAL24PC 23 +#define R_PPC_UADDR32 24 +#define R_PPC_UADDR16 25 +#define R_PPC_REL32 26 +#define R_PPC_PLT32 27 +#define R_PPC_PLTREL32 28 +#define R_PPC_PLT16_LO 29 +#define R_PPC_PLT16_HI 30 +#define R_PPC_PLT16_HA 31 +#define R_PPC_SDAREL16 32 +#define R_PPC_SECTOFF 33 +#define R_PPC_SECTOFF_LO 34 +#define R_PPC_SECTOFF_HI 35 +#define R_PPC_SECTOFF_HA 36 + +/* PowerPC relocations defined for the TLS access ABI. */ +#define R_PPC_TLS 67 /* none (sym+add)@tls */ +#define R_PPC_DTPMOD32 68 /* word32 (sym+add)@dtpmod */ +#define R_PPC_TPREL16 69 /* half16* (sym+add)@tprel */ +#define R_PPC_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ +#define R_PPC_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ +#define R_PPC_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ +#define R_PPC_TPREL32 73 /* word32 (sym+add)@tprel */ +#define R_PPC_DTPREL16 74 /* half16* (sym+add)@dtprel */ +#define R_PPC_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ +#define R_PPC_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ +#define R_PPC_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ +#define R_PPC_DTPREL32 78 /* word32 (sym+add)@dtprel */ +#define R_PPC_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ +#define R_PPC_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ +#define R_PPC_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ +#define R_PPC_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ +#define R_PPC_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ +#define R_PPC_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ +#define R_PPC_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ +#define R_PPC_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ +#define R_PPC_GOT_TPREL16 87 /* half16* (sym+add)@got@tprel */ +#define R_PPC_GOT_TPREL16_LO 88 /* half16 (sym+add)@got@tprel@l */ +#define R_PPC_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ +#define R_PPC_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ +#define R_PPC_GOT_DTPREL16 91 /* half16* (sym+add)@got@dtprel */ +#define R_PPC_GOT_DTPREL16_LO 92 /* half16* (sym+add)@got@dtprel@l */ +#define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */ +#define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */ +#define R_PPC_TLSGD 95 /* none (sym+add)@tlsgd */ +#define R_PPC_TLSLD 96 /* none (sym+add)@tlsld */ + +/* The remaining relocs are from the Embedded ELF ABI, and are not + in the SVR4 ELF ABI. */ +#define R_PPC_EMB_NADDR32 101 +#define R_PPC_EMB_NADDR16 102 +#define R_PPC_EMB_NADDR16_LO 103 +#define R_PPC_EMB_NADDR16_HI 104 +#define R_PPC_EMB_NADDR16_HA 105 +#define R_PPC_EMB_SDAI16 106 +#define R_PPC_EMB_SDA2I16 107 +#define R_PPC_EMB_SDA2REL 108 +#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ +#define R_PPC_EMB_MRKREF 110 +#define R_PPC_EMB_RELSEC16 111 +#define R_PPC_EMB_RELST_LO 112 +#define R_PPC_EMB_RELST_HI 113 +#define R_PPC_EMB_RELST_HA 114 +#define R_PPC_EMB_BIT_FLD 115 +#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ + +/* Diab tool relocations. */ +#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ +#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ +#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ +#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ +#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ +#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ + +/* GNU extension to support local ifunc. */ +#define R_PPC_IRELATIVE 248 + +/* GNU relocs used in PIC code sequences. */ +#define R_PPC_REL16 249 /* half16 (sym+add-.) */ +#define R_PPC_REL16_LO 250 /* half16 (sym+add-.)@l */ +#define R_PPC_REL16_HI 251 /* half16 (sym+add-.)@h */ +#define R_PPC_REL16_HA 252 /* half16 (sym+add-.)@ha */ + +/* This is a phony reloc to handle any old fashioned TOC16 references + that may still be in object files. */ +#define R_PPC_TOC16 255 + +/* PowerPC specific values for the Dyn d_tag field. */ +#define DT_PPC_GOT (DT_LOPROC + 0) +#define DT_PPC_OPT (DT_LOPROC + 1) +#define DT_PPC_NUM 2 + +/* PowerPC specific values for the DT_PPC_OPT Dyn entry. */ +#define PPC_OPT_TLS 1 + +/* PowerPC64 relocations defined by the ABIs */ +#define R_PPC64_NONE R_PPC_NONE +#define R_PPC64_ADDR32 R_PPC_ADDR32 /* 32bit absolute address */ +#define R_PPC64_ADDR24 R_PPC_ADDR24 /* 26bit address, word aligned */ +#define R_PPC64_ADDR16 R_PPC_ADDR16 /* 16bit absolute address */ +#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO /* lower 16bits of address */ +#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI /* high 16bits of address. */ +#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA /* adjusted high 16bits. */ +#define R_PPC64_ADDR14 R_PPC_ADDR14 /* 16bit address, word aligned */ +#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN +#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN +#define R_PPC64_REL24 R_PPC_REL24 /* PC-rel. 26 bit, word aligned */ +#define R_PPC64_REL14 R_PPC_REL14 /* PC relative 16 bit */ +#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN +#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN +#define R_PPC64_GOT16 R_PPC_GOT16 +#define R_PPC64_GOT16_LO R_PPC_GOT16_LO +#define R_PPC64_GOT16_HI R_PPC_GOT16_HI +#define R_PPC64_GOT16_HA R_PPC_GOT16_HA + +#define R_PPC64_COPY R_PPC_COPY +#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT +#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT +#define R_PPC64_RELATIVE R_PPC_RELATIVE + +#define R_PPC64_UADDR32 R_PPC_UADDR32 +#define R_PPC64_UADDR16 R_PPC_UADDR16 +#define R_PPC64_REL32 R_PPC_REL32 +#define R_PPC64_PLT32 R_PPC_PLT32 +#define R_PPC64_PLTREL32 R_PPC_PLTREL32 +#define R_PPC64_PLT16_LO R_PPC_PLT16_LO +#define R_PPC64_PLT16_HI R_PPC_PLT16_HI +#define R_PPC64_PLT16_HA R_PPC_PLT16_HA + +#define R_PPC64_SECTOFF R_PPC_SECTOFF +#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO +#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI +#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA +#define R_PPC64_ADDR30 37 /* word30 (S + A - P) >> 2 */ +#define R_PPC64_ADDR64 38 /* doubleword64 S + A */ +#define R_PPC64_ADDR16_HIGHER 39 /* half16 #higher(S + A) */ +#define R_PPC64_ADDR16_HIGHERA 40 /* half16 #highera(S + A) */ +#define R_PPC64_ADDR16_HIGHEST 41 /* half16 #highest(S + A) */ +#define R_PPC64_ADDR16_HIGHESTA 42 /* half16 #highesta(S + A) */ +#define R_PPC64_UADDR64 43 /* doubleword64 S + A */ +#define R_PPC64_REL64 44 /* doubleword64 S + A - P */ +#define R_PPC64_PLT64 45 /* doubleword64 L + A */ +#define R_PPC64_PLTREL64 46 /* doubleword64 L + A - P */ +#define R_PPC64_TOC16 47 /* half16* S + A - .TOC */ +#define R_PPC64_TOC16_LO 48 /* half16 #lo(S + A - .TOC.) */ +#define R_PPC64_TOC16_HI 49 /* half16 #hi(S + A - .TOC.) */ +#define R_PPC64_TOC16_HA 50 /* half16 #ha(S + A - .TOC.) */ +#define R_PPC64_TOC 51 /* doubleword64 .TOC */ +#define R_PPC64_PLTGOT16 52 /* half16* M + A */ +#define R_PPC64_PLTGOT16_LO 53 /* half16 #lo(M + A) */ +#define R_PPC64_PLTGOT16_HI 54 /* half16 #hi(M + A) */ +#define R_PPC64_PLTGOT16_HA 55 /* half16 #ha(M + A) */ + +#define R_PPC64_ADDR16_DS 56 /* half16ds* (S + A) >> 2 */ +#define R_PPC64_ADDR16_LO_DS 57 /* half16ds #lo(S + A) >> 2 */ +#define R_PPC64_GOT16_DS 58 /* half16ds* (G + A) >> 2 */ +#define R_PPC64_GOT16_LO_DS 59 /* half16ds #lo(G + A) >> 2 */ +#define R_PPC64_PLT16_LO_DS 60 /* half16ds #lo(L + A) >> 2 */ +#define R_PPC64_SECTOFF_DS 61 /* half16ds* (R + A) >> 2 */ +#define R_PPC64_SECTOFF_LO_DS 62 /* half16ds #lo(R + A) >> 2 */ +#define R_PPC64_TOC16_DS 63 /* half16ds* (S + A - .TOC.) >> 2 */ +#define R_PPC64_TOC16_LO_DS 64 /* half16ds #lo(S + A - .TOC.) >> 2 */ +#define R_PPC64_PLTGOT16_DS 65 /* half16ds* (M + A) >> 2 */ +#define R_PPC64_PLTGOT16_LO_DS 66 /* half16ds #lo(M + A) >> 2 */ + +/* PowerPC64 relocations defined for the TLS access ABI. */ +#define R_PPC64_TLS 67 /* none (sym+add)@tls */ +#define R_PPC64_DTPMOD64 68 /* doubleword64 (sym+add)@dtpmod */ +#define R_PPC64_TPREL16 69 /* half16* (sym+add)@tprel */ +#define R_PPC64_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ +#define R_PPC64_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ +#define R_PPC64_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ +#define R_PPC64_TPREL64 73 /* doubleword64 (sym+add)@tprel */ +#define R_PPC64_DTPREL16 74 /* half16* (sym+add)@dtprel */ +#define R_PPC64_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ +#define R_PPC64_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ +#define R_PPC64_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ +#define R_PPC64_DTPREL64 78 /* doubleword64 (sym+add)@dtprel */ +#define R_PPC64_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ +#define R_PPC64_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ +#define R_PPC64_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ +#define R_PPC64_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ +#define R_PPC64_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ +#define R_PPC64_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ +#define R_PPC64_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ +#define R_PPC64_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ +#define R_PPC64_GOT_TPREL16_DS 87 /* half16ds* (sym+add)@got@tprel */ +#define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */ +#define R_PPC64_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ +#define R_PPC64_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ +#define R_PPC64_GOT_DTPREL16_DS 91 /* half16ds* (sym+add)@got@dtprel */ +#define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */ +#define R_PPC64_GOT_DTPREL16_HI 93 /* half16 (sym+add)@got@dtprel@h */ +#define R_PPC64_GOT_DTPREL16_HA 94 /* half16 (sym+add)@got@dtprel@ha */ +#define R_PPC64_TPREL16_DS 95 /* half16ds* (sym+add)@tprel */ +#define R_PPC64_TPREL16_LO_DS 96 /* half16ds (sym+add)@tprel@l */ +#define R_PPC64_TPREL16_HIGHER 97 /* half16 (sym+add)@tprel@higher */ +#define R_PPC64_TPREL16_HIGHERA 98 /* half16 (sym+add)@tprel@highera */ +#define R_PPC64_TPREL16_HIGHEST 99 /* half16 (sym+add)@tprel@highest */ +#define R_PPC64_TPREL16_HIGHESTA 100 /* half16 (sym+add)@tprel@highesta */ +#define R_PPC64_DTPREL16_DS 101 /* half16ds* (sym+add)@dtprel */ +#define R_PPC64_DTPREL16_LO_DS 102 /* half16ds (sym+add)@dtprel@l */ +#define R_PPC64_DTPREL16_HIGHER 103 /* half16 (sym+add)@dtprel@higher */ +#define R_PPC64_DTPREL16_HIGHERA 104 /* half16 (sym+add)@dtprel@highera */ +#define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */ +#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */ +#define R_PPC64_TLSGD 107 /* none (sym+add)@tlsgd */ +#define R_PPC64_TLSLD 108 /* none (sym+add)@tlsld */ +#define R_PPC64_TOCSAVE 109 /* none */ + +/* Added when HA and HI relocs were changed to report overflows. */ +#define R_PPC64_ADDR16_HIGH 110 +#define R_PPC64_ADDR16_HIGHA 111 +#define R_PPC64_TPREL16_HIGH 112 +#define R_PPC64_TPREL16_HIGHA 113 +#define R_PPC64_DTPREL16_HIGH 114 +#define R_PPC64_DTPREL16_HIGHA 115 + +/* GNU extension to support local ifunc. */ +#define R_PPC64_JMP_IREL 247 +#define R_PPC64_IRELATIVE 248 +#define R_PPC64_REL16 249 /* half16 (sym+add-.) */ +#define R_PPC64_REL16_LO 250 /* half16 (sym+add-.)@l */ +#define R_PPC64_REL16_HI 251 /* half16 (sym+add-.)@h */ +#define R_PPC64_REL16_HA 252 /* half16 (sym+add-.)@ha */ + +/* e_flags bits specifying ABI. + 1 for original function descriptor using ABI, + 2 for revised ABI without function descriptors, + 0 for unspecified or not using any features affected by the differences. */ +#define EF_PPC64_ABI 3 + +/* PowerPC64 specific values for the Dyn d_tag field. */ +#define DT_PPC64_GLINK (DT_LOPROC + 0) +#define DT_PPC64_OPD (DT_LOPROC + 1) +#define DT_PPC64_OPDSZ (DT_LOPROC + 2) +#define DT_PPC64_OPT (DT_LOPROC + 3) +#define DT_PPC64_NUM 4 + +/* PowerPC64 specific bits in the DT_PPC64_OPT Dyn entry. */ +#define PPC64_OPT_TLS 1 +#define PPC64_OPT_MULTI_TOC 2 +#define PPC64_OPT_LOCALENTRY 4 + +/* PowerPC64 specific values for the Elf64_Sym st_other field. */ +#define STO_PPC64_LOCAL_BIT 5 +#define STO_PPC64_LOCAL_MASK (7 << STO_PPC64_LOCAL_BIT) +#define PPC64_LOCAL_ENTRY_OFFSET(other) \ + (((1 << (((other) & STO_PPC64_LOCAL_MASK) >> STO_PPC64_LOCAL_BIT)) >> 2) << 2) + + +/* ARM specific declarations */ + +/* Processor specific flags for the ELF header e_flags field. */ +#define EF_ARM_RELEXEC 0x01 +#define EF_ARM_HASENTRY 0x02 +#define EF_ARM_INTERWORK 0x04 +#define EF_ARM_APCS_26 0x08 +#define EF_ARM_APCS_FLOAT 0x10 +#define EF_ARM_PIC 0x20 +#define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */ +#define EF_ARM_NEW_ABI 0x80 +#define EF_ARM_OLD_ABI 0x100 +#define EF_ARM_SOFT_FLOAT 0x200 +#define EF_ARM_VFP_FLOAT 0x400 +#define EF_ARM_MAVERICK_FLOAT 0x800 + +#define EF_ARM_ABI_FLOAT_SOFT 0x200 /* NB conflicts with EF_ARM_SOFT_FLOAT */ +#define EF_ARM_ABI_FLOAT_HARD 0x400 /* NB conflicts with EF_ARM_VFP_FLOAT */ + + +/* Other constants defined in the ARM ELF spec. version B-01. */ +/* NB. These conflict with values defined above. */ +#define EF_ARM_SYMSARESORTED 0x04 +#define EF_ARM_DYNSYMSUSESEGIDX 0x08 +#define EF_ARM_MAPSYMSFIRST 0x10 +#define EF_ARM_EABIMASK 0XFF000000 + +/* Constants defined in AAELF. */ +#define EF_ARM_BE8 0x00800000 +#define EF_ARM_LE8 0x00400000 + +#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) +#define EF_ARM_EABI_UNKNOWN 0x00000000 +#define EF_ARM_EABI_VER1 0x01000000 +#define EF_ARM_EABI_VER2 0x02000000 +#define EF_ARM_EABI_VER3 0x03000000 +#define EF_ARM_EABI_VER4 0x04000000 +#define EF_ARM_EABI_VER5 0x05000000 + +/* Additional symbol types for Thumb. */ +#define STT_ARM_TFUNC STT_LOPROC /* A Thumb function. */ +#define STT_ARM_16BIT STT_HIPROC /* A Thumb label. */ + +/* ARM-specific values for sh_flags */ +#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */ +#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined + in the input to a link step. */ + +/* ARM-specific program header flags */ +#define PF_ARM_SB 0x10000000 /* Segment contains the location + addressed by the static base. */ +#define PF_ARM_PI 0x20000000 /* Position-independent segment. */ +#define PF_ARM_ABS 0x40000000 /* Absolute segment. */ + +/* Processor specific values for the Phdr p_type field. */ +#define PT_ARM_EXIDX (PT_LOPROC + 1) /* ARM unwind segment. */ + +/* Processor specific values for the Shdr sh_type field. */ +#define SHT_ARM_EXIDX (SHT_LOPROC + 1) /* ARM unwind section. */ +#define SHT_ARM_PREEMPTMAP (SHT_LOPROC + 2) /* Preemption details. */ +#define SHT_ARM_ATTRIBUTES (SHT_LOPROC + 3) /* ARM attributes section. */ + + +/* AArch64 relocs. */ + +#define R_AARCH64_NONE 0 /* No relocation. */ + +/* ILP32 AArch64 relocs. */ +#define R_AARCH64_P32_ABS32 1 /* Direct 32 bit. */ +#define R_AARCH64_P32_COPY 180 /* Copy symbol at runtime. */ +#define R_AARCH64_P32_GLOB_DAT 181 /* Create GOT entry. */ +#define R_AARCH64_P32_JUMP_SLOT 182 /* Create PLT entry. */ +#define R_AARCH64_P32_RELATIVE 183 /* Adjust by program base. */ +#define R_AARCH64_P32_TLS_DTPMOD 184 /* Module number, 32 bit. */ +#define R_AARCH64_P32_TLS_DTPREL 185 /* Module-relative offset, 32 bit. */ +#define R_AARCH64_P32_TLS_TPREL 186 /* TP-relative offset, 32 bit. */ +#define R_AARCH64_P32_TLSDESC 187 /* TLS Descriptor. */ +#define R_AARCH64_P32_IRELATIVE 188 /* STT_GNU_IFUNC relocation. */ + +/* LP64 AArch64 relocs. */ +#define R_AARCH64_ABS64 257 /* Direct 64 bit. */ +#define R_AARCH64_ABS32 258 /* Direct 32 bit. */ +#define R_AARCH64_ABS16 259 /* Direct 16-bit. */ +#define R_AARCH64_PREL64 260 /* PC-relative 64-bit. */ +#define R_AARCH64_PREL32 261 /* PC-relative 32-bit. */ +#define R_AARCH64_PREL16 262 /* PC-relative 16-bit. */ +#define R_AARCH64_MOVW_UABS_G0 263 /* Dir. MOVZ imm. from bits 15:0. */ +#define R_AARCH64_MOVW_UABS_G0_NC 264 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_UABS_G1 265 /* Dir. MOVZ imm. from bits 31:16. */ +#define R_AARCH64_MOVW_UABS_G1_NC 266 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_UABS_G2 267 /* Dir. MOVZ imm. from bits 47:32. */ +#define R_AARCH64_MOVW_UABS_G2_NC 268 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_UABS_G3 269 /* Dir. MOV{K,Z} imm. from 63:48. */ +#define R_AARCH64_MOVW_SABS_G0 270 /* Dir. MOV{N,Z} imm. from 15:0. */ +#define R_AARCH64_MOVW_SABS_G1 271 /* Dir. MOV{N,Z} imm. from 31:16. */ +#define R_AARCH64_MOVW_SABS_G2 272 /* Dir. MOV{N,Z} imm. from 47:32. */ +#define R_AARCH64_LD_PREL_LO19 273 /* PC-rel. LD imm. from bits 20:2. */ +#define R_AARCH64_ADR_PREL_LO21 274 /* PC-rel. ADR imm. from bits 20:0. */ +#define R_AARCH64_ADR_PREL_PG_HI21 275 /* Page-rel. ADRP imm. from 32:12. */ +#define R_AARCH64_ADR_PREL_PG_HI21_NC 276 /* Likewise; no overflow check. */ +#define R_AARCH64_ADD_ABS_LO12_NC 277 /* Dir. ADD imm. from bits 11:0. */ +#define R_AARCH64_LDST8_ABS_LO12_NC 278 /* Likewise for LD/ST; no check. */ +#define R_AARCH64_TSTBR14 279 /* PC-rel. TBZ/TBNZ imm. from 15:2. */ +#define R_AARCH64_CONDBR19 280 /* PC-rel. cond. br. imm. from 20:2. */ +#define R_AARCH64_JUMP26 282 /* PC-rel. B imm. from bits 27:2. */ +#define R_AARCH64_CALL26 283 /* Likewise for CALL. */ +#define R_AARCH64_LDST16_ABS_LO12_NC 284 /* Dir. ADD imm. from bits 11:1. */ +#define R_AARCH64_LDST32_ABS_LO12_NC 285 /* Likewise for bits 11:2. */ +#define R_AARCH64_LDST64_ABS_LO12_NC 286 /* Likewise for bits 11:3. */ +#define R_AARCH64_MOVW_PREL_G0 287 /* PC-rel. MOV{N,Z} imm. from 15:0. */ +#define R_AARCH64_MOVW_PREL_G0_NC 288 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_PREL_G1 289 /* PC-rel. MOV{N,Z} imm. from 31:16. */ +#define R_AARCH64_MOVW_PREL_G1_NC 290 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_PREL_G2 291 /* PC-rel. MOV{N,Z} imm. from 47:32. */ +#define R_AARCH64_MOVW_PREL_G2_NC 292 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_PREL_G3 293 /* PC-rel. MOV{N,Z} imm. from 63:48. */ +#define R_AARCH64_LDST128_ABS_LO12_NC 299 /* Dir. ADD imm. from bits 11:4. */ +#define R_AARCH64_MOVW_GOTOFF_G0 300 /* GOT-rel. off. MOV{N,Z} imm. 15:0. */ +#define R_AARCH64_MOVW_GOTOFF_G0_NC 301 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_GOTOFF_G1 302 /* GOT-rel. o. MOV{N,Z} imm. 31:16. */ +#define R_AARCH64_MOVW_GOTOFF_G1_NC 303 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_GOTOFF_G2 304 /* GOT-rel. o. MOV{N,Z} imm. 47:32. */ +#define R_AARCH64_MOVW_GOTOFF_G2_NC 305 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_GOTOFF_G3 306 /* GOT-rel. o. MOV{N,Z} imm. 63:48. */ +#define R_AARCH64_GOTREL64 307 /* GOT-relative 64-bit. */ +#define R_AARCH64_GOTREL32 308 /* GOT-relative 32-bit. */ +#define R_AARCH64_GOT_LD_PREL19 309 /* PC-rel. GOT off. load imm. 20:2. */ +#define R_AARCH64_LD64_GOTOFF_LO15 310 /* GOT-rel. off. LD/ST imm. 14:3. */ +#define R_AARCH64_ADR_GOT_PAGE 311 /* P-page-rel. GOT off. ADRP 32:12. */ +#define R_AARCH64_LD64_GOT_LO12_NC 312 /* Dir. GOT off. LD/ST imm. 11:3. */ +#define R_AARCH64_LD64_GOTPAGE_LO15 313 /* GOT-page-rel. GOT off. LD/ST 14:3 */ +#define R_AARCH64_TLSGD_ADR_PREL21 512 /* PC-relative ADR imm. 20:0. */ +#define R_AARCH64_TLSGD_ADR_PAGE21 513 /* page-rel. ADRP imm. 32:12. */ +#define R_AARCH64_TLSGD_ADD_LO12_NC 514 /* direct ADD imm. from 11:0. */ +#define R_AARCH64_TLSGD_MOVW_G1 515 /* GOT-rel. MOV{N,Z} 31:16. */ +#define R_AARCH64_TLSGD_MOVW_G0_NC 516 /* GOT-rel. MOVK imm. 15:0. */ +#define R_AARCH64_TLSLD_ADR_PREL21 517 /* Like 512; local dynamic model. */ +#define R_AARCH64_TLSLD_ADR_PAGE21 518 /* Like 513; local dynamic model. */ +#define R_AARCH64_TLSLD_ADD_LO12_NC 519 /* Like 514; local dynamic model. */ +#define R_AARCH64_TLSLD_MOVW_G1 520 /* Like 515; local dynamic model. */ +#define R_AARCH64_TLSLD_MOVW_G0_NC 521 /* Like 516; local dynamic model. */ +#define R_AARCH64_TLSLD_LD_PREL19 522 /* TLS PC-rel. load imm. 20:2. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G2 523 /* TLS DTP-rel. MOV{N,Z} 47:32. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G1 524 /* TLS DTP-rel. MOV{N,Z} 31:16. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G1_NC 525 /* Likewise; MOVK; no check. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G0 526 /* TLS DTP-rel. MOV{N,Z} 15:0. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC 527 /* Likewise; MOVK; no check. */ +#define R_AARCH64_TLSLD_ADD_DTPREL_HI12 528 /* DTP-rel. ADD imm. from 23:12. */ +#define R_AARCH64_TLSLD_ADD_DTPREL_LO12 529 /* DTP-rel. ADD imm. from 11:0. */ +#define R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC 530 /* Likewise; no ovfl. check. */ +#define R_AARCH64_TLSLD_LDST8_DTPREL_LO12 531 /* DTP-rel. LD/ST imm. 11:0. */ +#define R_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC 532 /* Likewise; no check. */ +#define R_AARCH64_TLSLD_LDST16_DTPREL_LO12 533 /* DTP-rel. LD/ST imm. 11:1. */ +#define R_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC 534 /* Likewise; no check. */ +#define R_AARCH64_TLSLD_LDST32_DTPREL_LO12 535 /* DTP-rel. LD/ST imm. 11:2. */ +#define R_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC 536 /* Likewise; no check. */ +#define R_AARCH64_TLSLD_LDST64_DTPREL_LO12 537 /* DTP-rel. LD/ST imm. 11:3. */ +#define R_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC 538 /* Likewise; no check. */ +#define R_AARCH64_TLSIE_MOVW_GOTTPREL_G1 539 /* GOT-rel. MOV{N,Z} 31:16. */ +#define R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC 540 /* GOT-rel. MOVK 15:0. */ +#define R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 541 /* Page-rel. ADRP 32:12. */ +#define R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC 542 /* Direct LD off. 11:3. */ +#define R_AARCH64_TLSIE_LD_GOTTPREL_PREL19 543 /* PC-rel. load imm. 20:2. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G2 544 /* TLS TP-rel. MOV{N,Z} 47:32. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G1 545 /* TLS TP-rel. MOV{N,Z} 31:16. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G1_NC 546 /* Likewise; MOVK; no check. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G0 547 /* TLS TP-rel. MOV{N,Z} 15:0. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G0_NC 548 /* Likewise; MOVK; no check. */ +#define R_AARCH64_TLSLE_ADD_TPREL_HI12 549 /* TP-rel. ADD imm. 23:12. */ +#define R_AARCH64_TLSLE_ADD_TPREL_LO12 550 /* TP-rel. ADD imm. 11:0. */ +#define R_AARCH64_TLSLE_ADD_TPREL_LO12_NC 551 /* Likewise; no ovfl. check. */ +#define R_AARCH64_TLSLE_LDST8_TPREL_LO12 552 /* TP-rel. LD/ST off. 11:0. */ +#define R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC 553 /* Likewise; no ovfl. check. */ +#define R_AARCH64_TLSLE_LDST16_TPREL_LO12 554 /* TP-rel. LD/ST off. 11:1. */ +#define R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC 555 /* Likewise; no check. */ +#define R_AARCH64_TLSLE_LDST32_TPREL_LO12 556 /* TP-rel. LD/ST off. 11:2. */ +#define R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC 557 /* Likewise; no check. */ +#define R_AARCH64_TLSLE_LDST64_TPREL_LO12 558 /* TP-rel. LD/ST off. 11:3. */ +#define R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC 559 /* Likewise; no check. */ +#define R_AARCH64_TLSDESC_LD_PREL19 560 /* PC-rel. load immediate 20:2. */ +#define R_AARCH64_TLSDESC_ADR_PREL21 561 /* PC-rel. ADR immediate 20:0. */ +#define R_AARCH64_TLSDESC_ADR_PAGE21 562 /* Page-rel. ADRP imm. 32:12. */ +#define R_AARCH64_TLSDESC_LD64_LO12 563 /* Direct LD off. from 11:3. */ +#define R_AARCH64_TLSDESC_ADD_LO12 564 /* Direct ADD imm. from 11:0. */ +#define R_AARCH64_TLSDESC_OFF_G1 565 /* GOT-rel. MOV{N,Z} imm. 31:16. */ +#define R_AARCH64_TLSDESC_OFF_G0_NC 566 /* GOT-rel. MOVK imm. 15:0; no ck. */ +#define R_AARCH64_TLSDESC_LDR 567 /* Relax LDR. */ +#define R_AARCH64_TLSDESC_ADD 568 /* Relax ADD. */ +#define R_AARCH64_TLSDESC_CALL 569 /* Relax BLR. */ +#define R_AARCH64_TLSLE_LDST128_TPREL_LO12 570 /* TP-rel. LD/ST off. 11:4. */ +#define R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC 571 /* Likewise; no check. */ +#define R_AARCH64_TLSLD_LDST128_DTPREL_LO12 572 /* DTP-rel. LD/ST imm. 11:4. */ +#define R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC 573 /* Likewise; no check. */ +#define R_AARCH64_COPY 1024 /* Copy symbol at runtime. */ +#define R_AARCH64_GLOB_DAT 1025 /* Create GOT entry. */ +#define R_AARCH64_JUMP_SLOT 1026 /* Create PLT entry. */ +#define R_AARCH64_RELATIVE 1027 /* Adjust by program base. */ +#define R_AARCH64_TLS_DTPMOD 1028 /* Module number, 64 bit. */ +#define R_AARCH64_TLS_DTPREL 1029 /* Module-relative offset, 64 bit. */ +#define R_AARCH64_TLS_TPREL 1030 /* TP-relative offset, 64 bit. */ +#define R_AARCH64_TLSDESC 1031 /* TLS Descriptor. */ +#define R_AARCH64_IRELATIVE 1032 /* STT_GNU_IFUNC relocation. */ + +/* MTE memory tag segment type. */ +#define PT_AARCH64_MEMTAG_MTE (PT_LOPROC + 2) + +/* AArch64 specific values for the Dyn d_tag field. */ +#define DT_AARCH64_BTI_PLT (DT_LOPROC + 1) +#define DT_AARCH64_PAC_PLT (DT_LOPROC + 3) +#define DT_AARCH64_VARIANT_PCS (DT_LOPROC + 5) +#define DT_AARCH64_NUM 6 + +/* AArch64 specific values for the st_other field. */ +#define STO_AARCH64_VARIANT_PCS 0x80 + +/* ARM relocs. */ + +#define R_ARM_NONE 0 /* No reloc */ +#define R_ARM_PC24 1 /* Deprecated PC relative 26 + bit branch. */ +#define R_ARM_ABS32 2 /* Direct 32 bit */ +#define R_ARM_REL32 3 /* PC relative 32 bit */ +#define R_ARM_PC13 4 +#define R_ARM_ABS16 5 /* Direct 16 bit */ +#define R_ARM_ABS12 6 /* Direct 12 bit */ +#define R_ARM_THM_ABS5 7 /* Direct & 0x7C (LDR, STR). */ +#define R_ARM_ABS8 8 /* Direct 8 bit */ +#define R_ARM_SBREL32 9 +#define R_ARM_THM_PC22 10 /* PC relative 24 bit (Thumb32 BL). */ +#define R_ARM_THM_PC8 11 /* PC relative & 0x3FC + (Thumb16 LDR, ADD, ADR). */ +#define R_ARM_AMP_VCALL9 12 +#define R_ARM_SWI24 13 /* Obsolete static relocation. */ +#define R_ARM_TLS_DESC 13 /* Dynamic relocation. */ +#define R_ARM_THM_SWI8 14 /* Reserved. */ +#define R_ARM_XPC25 15 /* Reserved. */ +#define R_ARM_THM_XPC22 16 /* Reserved. */ +#define R_ARM_TLS_DTPMOD32 17 /* ID of module containing symbol */ +#define R_ARM_TLS_DTPOFF32 18 /* Offset in TLS block */ +#define R_ARM_TLS_TPOFF32 19 /* Offset in static TLS block */ +#define R_ARM_COPY 20 /* Copy symbol at runtime */ +#define R_ARM_GLOB_DAT 21 /* Create GOT entry */ +#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ +#define R_ARM_RELATIVE 23 /* Adjust by program base */ +#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */ +#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ +#define R_ARM_GOT32 26 /* 32 bit GOT entry */ +#define R_ARM_PLT32 27 /* Deprecated, 32 bit PLT address. */ +#define R_ARM_CALL 28 /* PC relative 24 bit (BL, BLX). */ +#define R_ARM_JUMP24 29 /* PC relative 24 bit + (B, BL). */ +#define R_ARM_THM_JUMP24 30 /* PC relative 24 bit (Thumb32 B.W). */ +#define R_ARM_BASE_ABS 31 /* Adjust by program base. */ +#define R_ARM_ALU_PCREL_7_0 32 /* Obsolete. */ +#define R_ARM_ALU_PCREL_15_8 33 /* Obsolete. */ +#define R_ARM_ALU_PCREL_23_15 34 /* Obsolete. */ +#define R_ARM_LDR_SBREL_11_0 35 /* Deprecated, prog. base relative. */ +#define R_ARM_ALU_SBREL_19_12 36 /* Deprecated, prog. base relative. */ +#define R_ARM_ALU_SBREL_27_20 37 /* Deprecated, prog. base relative. */ +#define R_ARM_TARGET1 38 +#define R_ARM_SBREL31 39 /* Program base relative. */ +#define R_ARM_V4BX 40 +#define R_ARM_TARGET2 41 +#define R_ARM_PREL31 42 /* 32 bit PC relative. */ +#define R_ARM_MOVW_ABS_NC 43 /* Direct 16-bit (MOVW). */ +#define R_ARM_MOVT_ABS 44 /* Direct high 16-bit (MOVT). */ +#define R_ARM_MOVW_PREL_NC 45 /* PC relative 16-bit (MOVW). */ +#define R_ARM_MOVT_PREL 46 /* PC relative (MOVT). */ +#define R_ARM_THM_MOVW_ABS_NC 47 /* Direct 16 bit (Thumb32 MOVW). */ +#define R_ARM_THM_MOVT_ABS 48 /* Direct high 16 bit + (Thumb32 MOVT). */ +#define R_ARM_THM_MOVW_PREL_NC 49 /* PC relative 16 bit + (Thumb32 MOVW). */ +#define R_ARM_THM_MOVT_PREL 50 /* PC relative high 16 bit + (Thumb32 MOVT). */ +#define R_ARM_THM_JUMP19 51 /* PC relative 20 bit + (Thumb32 B.W). */ +#define R_ARM_THM_JUMP6 52 /* PC relative X & 0x7E + (Thumb16 CBZ, CBNZ). */ +#define R_ARM_THM_ALU_PREL_11_0 53 /* PC relative 12 bit + (Thumb32 ADR.W). */ +#define R_ARM_THM_PC12 54 /* PC relative 12 bit + (Thumb32 LDR{D,SB,H,SH}). */ +#define R_ARM_ABS32_NOI 55 /* Direct 32-bit. */ +#define R_ARM_REL32_NOI 56 /* PC relative 32-bit. */ +#define R_ARM_ALU_PC_G0_NC 57 /* PC relative (ADD, SUB). */ +#define R_ARM_ALU_PC_G0 58 /* PC relative (ADD, SUB). */ +#define R_ARM_ALU_PC_G1_NC 59 /* PC relative (ADD, SUB). */ +#define R_ARM_ALU_PC_G1 60 /* PC relative (ADD, SUB). */ +#define R_ARM_ALU_PC_G2 61 /* PC relative (ADD, SUB). */ +#define R_ARM_LDR_PC_G1 62 /* PC relative (LDR,STR,LDRB,STRB). */ +#define R_ARM_LDR_PC_G2 63 /* PC relative (LDR,STR,LDRB,STRB). */ +#define R_ARM_LDRS_PC_G0 64 /* PC relative (STR{D,H}, + LDR{D,SB,H,SH}). */ +#define R_ARM_LDRS_PC_G1 65 /* PC relative (STR{D,H}, + LDR{D,SB,H,SH}). */ +#define R_ARM_LDRS_PC_G2 66 /* PC relative (STR{D,H}, + LDR{D,SB,H,SH}). */ +#define R_ARM_LDC_PC_G0 67 /* PC relative (LDC, STC). */ +#define R_ARM_LDC_PC_G1 68 /* PC relative (LDC, STC). */ +#define R_ARM_LDC_PC_G2 69 /* PC relative (LDC, STC). */ +#define R_ARM_ALU_SB_G0_NC 70 /* Program base relative (ADD,SUB). */ +#define R_ARM_ALU_SB_G0 71 /* Program base relative (ADD,SUB). */ +#define R_ARM_ALU_SB_G1_NC 72 /* Program base relative (ADD,SUB). */ +#define R_ARM_ALU_SB_G1 73 /* Program base relative (ADD,SUB). */ +#define R_ARM_ALU_SB_G2 74 /* Program base relative (ADD,SUB). */ +#define R_ARM_LDR_SB_G0 75 /* Program base relative (LDR, + STR, LDRB, STRB). */ +#define R_ARM_LDR_SB_G1 76 /* Program base relative + (LDR, STR, LDRB, STRB). */ +#define R_ARM_LDR_SB_G2 77 /* Program base relative + (LDR, STR, LDRB, STRB). */ +#define R_ARM_LDRS_SB_G0 78 /* Program base relative + (LDR, STR, LDRB, STRB). */ +#define R_ARM_LDRS_SB_G1 79 /* Program base relative + (LDR, STR, LDRB, STRB). */ +#define R_ARM_LDRS_SB_G2 80 /* Program base relative + (LDR, STR, LDRB, STRB). */ +#define R_ARM_LDC_SB_G0 81 /* Program base relative (LDC,STC). */ +#define R_ARM_LDC_SB_G1 82 /* Program base relative (LDC,STC). */ +#define R_ARM_LDC_SB_G2 83 /* Program base relative (LDC,STC). */ +#define R_ARM_MOVW_BREL_NC 84 /* Program base relative 16 + bit (MOVW). */ +#define R_ARM_MOVT_BREL 85 /* Program base relative high + 16 bit (MOVT). */ +#define R_ARM_MOVW_BREL 86 /* Program base relative 16 + bit (MOVW). */ +#define R_ARM_THM_MOVW_BREL_NC 87 /* Program base relative 16 + bit (Thumb32 MOVW). */ +#define R_ARM_THM_MOVT_BREL 88 /* Program base relative high + 16 bit (Thumb32 MOVT). */ +#define R_ARM_THM_MOVW_BREL 89 /* Program base relative 16 + bit (Thumb32 MOVW). */ +#define R_ARM_TLS_GOTDESC 90 +#define R_ARM_TLS_CALL 91 +#define R_ARM_TLS_DESCSEQ 92 /* TLS relaxation. */ +#define R_ARM_THM_TLS_CALL 93 +#define R_ARM_PLT32_ABS 94 +#define R_ARM_GOT_ABS 95 /* GOT entry. */ +#define R_ARM_GOT_PREL 96 /* PC relative GOT entry. */ +#define R_ARM_GOT_BREL12 97 /* GOT entry relative to GOT + origin (LDR). */ +#define R_ARM_GOTOFF12 98 /* 12 bit, GOT entry relative + to GOT origin (LDR, STR). */ +#define R_ARM_GOTRELAX 99 +#define R_ARM_GNU_VTENTRY 100 +#define R_ARM_GNU_VTINHERIT 101 +#define R_ARM_THM_PC11 102 /* PC relative & 0xFFE (Thumb16 B). */ +#define R_ARM_THM_PC9 103 /* PC relative & 0x1FE + (Thumb16 B/B). */ +#define R_ARM_TLS_GD32 104 /* PC-rel 32 bit for global dynamic + thread local data */ +#define R_ARM_TLS_LDM32 105 /* PC-rel 32 bit for local dynamic + thread local data */ +#define R_ARM_TLS_LDO32 106 /* 32 bit offset relative to TLS + block */ +#define R_ARM_TLS_IE32 107 /* PC-rel 32 bit for GOT entry of + static TLS block offset */ +#define R_ARM_TLS_LE32 108 /* 32 bit offset relative to static + TLS block */ +#define R_ARM_TLS_LDO12 109 /* 12 bit relative to TLS + block (LDR, STR). */ +#define R_ARM_TLS_LE12 110 /* 12 bit relative to static + TLS block (LDR, STR). */ +#define R_ARM_TLS_IE12GP 111 /* 12 bit GOT entry relative + to GOT origin (LDR). */ +#define R_ARM_ME_TOO 128 /* Obsolete. */ +#define R_ARM_THM_TLS_DESCSEQ 129 +#define R_ARM_THM_TLS_DESCSEQ16 129 +#define R_ARM_THM_TLS_DESCSEQ32 130 +#define R_ARM_THM_GOT_BREL12 131 /* GOT entry relative to GOT + origin, 12 bit (Thumb32 LDR). */ +#define R_ARM_IRELATIVE 160 +#define R_ARM_RXPC25 249 +#define R_ARM_RSBREL32 250 +#define R_ARM_THM_RPC22 251 +#define R_ARM_RREL32 252 +#define R_ARM_RABS22 253 +#define R_ARM_RPC24 254 +#define R_ARM_RBASE 255 +/* Keep this the last entry. */ +#define R_ARM_NUM 256 + +/* C-SKY */ +#define R_CKCORE_NONE 0 /* no reloc */ +#define R_CKCORE_ADDR32 1 /* direct 32 bit (S + A) */ +#define R_CKCORE_PCRELIMM8BY4 2 /* disp ((S + A - P) >> 2) & 0xff */ +#define R_CKCORE_PCRELIMM11BY2 3 /* disp ((S + A - P) >> 1) & 0x7ff */ +#define R_CKCORE_PCREL32 5 /* 32-bit rel (S + A - P) */ +#define R_CKCORE_PCRELJSR_IMM11BY2 6 /* disp ((S + A - P) >>1) & 0x7ff */ +#define R_CKCORE_RELATIVE 9 /* 32 bit adjust program base(B + A)*/ +#define R_CKCORE_COPY 10 /* 32 bit adjust by program base */ +#define R_CKCORE_GLOB_DAT 11 /* off between got and sym (S) */ +#define R_CKCORE_JUMP_SLOT 12 /* PLT entry (S) */ +#define R_CKCORE_GOTOFF 13 /* offset to GOT (S + A - GOT) */ +#define R_CKCORE_GOTPC 14 /* PC offset to GOT (GOT + A - P) */ +#define R_CKCORE_GOT32 15 /* 32 bit GOT entry (G) */ +#define R_CKCORE_PLT32 16 /* 32 bit PLT entry (G) */ +#define R_CKCORE_ADDRGOT 17 /* GOT entry in GLOB_DAT (GOT + G) */ +#define R_CKCORE_ADDRPLT 18 /* PLT entry in GLOB_DAT (GOT + G) */ +#define R_CKCORE_PCREL_IMM26BY2 19 /* ((S + A - P) >> 1) & 0x3ffffff */ +#define R_CKCORE_PCREL_IMM16BY2 20 /* disp ((S + A - P) >> 1) & 0xffff */ +#define R_CKCORE_PCREL_IMM16BY4 21 /* disp ((S + A - P) >> 2) & 0xffff */ +#define R_CKCORE_PCREL_IMM10BY2 22 /* disp ((S + A - P) >> 1) & 0x3ff */ +#define R_CKCORE_PCREL_IMM10BY4 23 /* disp ((S + A - P) >> 2) & 0x3ff */ +#define R_CKCORE_ADDR_HI16 24 /* high & low 16 bit ADDR */ + /* ((S + A) >> 16) & 0xffff */ +#define R_CKCORE_ADDR_LO16 25 /* (S + A) & 0xffff */ +#define R_CKCORE_GOTPC_HI16 26 /* high & low 16 bit GOTPC */ + /* ((GOT + A - P) >> 16) & 0xffff */ +#define R_CKCORE_GOTPC_LO16 27 /* (GOT + A - P) & 0xffff */ +#define R_CKCORE_GOTOFF_HI16 28 /* high & low 16 bit GOTOFF */ + /* ((S + A - GOT) >> 16) & 0xffff */ +#define R_CKCORE_GOTOFF_LO16 29 /* (S + A - GOT) & 0xffff */ +#define R_CKCORE_GOT12 30 /* 12 bit disp GOT entry (G) */ +#define R_CKCORE_GOT_HI16 31 /* high & low 16 bit GOT */ + /* (G >> 16) & 0xffff */ +#define R_CKCORE_GOT_LO16 32 /* (G & 0xffff) */ +#define R_CKCORE_PLT12 33 /* 12 bit disp PLT entry (G) */ +#define R_CKCORE_PLT_HI16 34 /* high & low 16 bit PLT */ + /* (G >> 16) & 0xffff */ +#define R_CKCORE_PLT_LO16 35 /* G & 0xffff */ +#define R_CKCORE_ADDRGOT_HI16 36 /* high & low 16 bit ADDRGOT */ + /* (GOT + G * 4) & 0xffff */ +#define R_CKCORE_ADDRGOT_LO16 37 /* (GOT + G * 4) & 0xffff */ +#define R_CKCORE_ADDRPLT_HI16 38 /* high & low 16 bit ADDRPLT */ + /* ((GOT + G * 4) >> 16) & 0xFFFF */ +#define R_CKCORE_ADDRPLT_LO16 39 /* (GOT+G*4) & 0xffff */ +#define R_CKCORE_PCREL_JSR_IMM26BY2 40 /* disp ((S+A-P) >>1) & x3ffffff */ +#define R_CKCORE_TOFFSET_LO16 41 /* (S+A-BTEXT) & 0xffff */ +#define R_CKCORE_DOFFSET_LO16 42 /* (S+A-BTEXT) & 0xffff */ +#define R_CKCORE_PCREL_IMM18BY2 43 /* disp ((S+A-P) >>1) & 0x3ffff */ +#define R_CKCORE_DOFFSET_IMM18 44 /* disp (S+A-BDATA) & 0x3ffff */ +#define R_CKCORE_DOFFSET_IMM18BY2 45 /* disp ((S+A-BDATA)>>1) & 0x3ffff */ +#define R_CKCORE_DOFFSET_IMM18BY4 46 /* disp ((S+A-BDATA)>>2) & 0x3ffff */ +#define R_CKCORE_GOT_IMM18BY4 48 /* disp (G >> 2) */ +#define R_CKCORE_PLT_IMM18BY4 49 /* disp (G >> 2) */ +#define R_CKCORE_PCREL_IMM7BY4 50 /* disp ((S+A-P) >>2) & 0x7f */ +#define R_CKCORE_TLS_LE32 51 /* 32 bit offset to TLS block */ +#define R_CKCORE_TLS_IE32 52 +#define R_CKCORE_TLS_GD32 53 +#define R_CKCORE_TLS_LDM32 54 +#define R_CKCORE_TLS_LDO32 55 +#define R_CKCORE_TLS_DTPMOD32 56 +#define R_CKCORE_TLS_DTPOFF32 57 +#define R_CKCORE_TLS_TPOFF32 58 + +/* C-SKY elf header definition. */ +#define EF_CSKY_ABIMASK 0XF0000000 +#define EF_CSKY_OTHER 0X0FFF0000 +#define EF_CSKY_PROCESSOR 0X0000FFFF + +#define EF_CSKY_ABIV1 0X10000000 +#define EF_CSKY_ABIV2 0X20000000 + +/* C-SKY attributes section. */ +#define SHT_CSKY_ATTRIBUTES (SHT_LOPROC + 1) + +/* IA-64 specific declarations. */ + +/* Processor specific flags for the Ehdr e_flags field. */ +#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */ +#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */ +#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */ + +/* Processor specific values for the Phdr p_type field. */ +#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */ +#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */ +#define PT_IA_64_HP_OPT_ANOT (PT_LOOS + 0x12) +#define PT_IA_64_HP_HSL_ANOT (PT_LOOS + 0x13) +#define PT_IA_64_HP_STACK (PT_LOOS + 0x14) + +/* Processor specific flags for the Phdr p_flags field. */ +#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */ + +/* Processor specific values for the Shdr sh_type field. */ +#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */ +#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */ + +/* Processor specific flags for the Shdr sh_flags field. */ +#define SHF_IA_64_SHORT 0x10000000 /* section near gp */ +#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */ + +/* Processor specific values for the Dyn d_tag field. */ +#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) +#define DT_IA_64_NUM 1 + +/* IA-64 relocations. */ +#define R_IA64_NONE 0x00 /* none */ +#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */ +#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */ +#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */ +#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */ +#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */ +#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */ +#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */ +#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */ +#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */ +#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */ +#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */ +#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */ +#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */ +#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */ +#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */ +#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */ +#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */ +#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */ +#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */ +#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */ +#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */ +#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */ +#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */ +#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */ +#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */ +#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */ +#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */ +#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */ +#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */ +#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */ +#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */ +#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */ +#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */ +#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */ +#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */ +#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */ +#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */ +#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */ +#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */ +#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */ +#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */ +#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */ +#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */ +#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */ +#define R_IA64_REL32MSB 0x6c /* data 4 + REL */ +#define R_IA64_REL32LSB 0x6d /* data 4 + REL */ +#define R_IA64_REL64MSB 0x6e /* data 8 + REL */ +#define R_IA64_REL64LSB 0x6f /* data 8 + REL */ +#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */ +#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */ +#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */ +#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */ +#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */ +#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */ +#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */ +#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */ +#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */ +#define R_IA64_COPY 0x84 /* copy relocation */ +#define R_IA64_SUB 0x85 /* Addend and symbol difference */ +#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */ +#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */ +#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */ +#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */ +#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */ +#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */ +#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */ +#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */ +#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */ +#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */ +#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */ +#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */ +#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */ +#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */ +#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */ +#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */ +#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ + +/* SH specific declarations */ + +/* Processor specific flags for the ELF header e_flags field. */ +#define EF_SH_MACH_MASK 0x1f +#define EF_SH_UNKNOWN 0x0 +#define EF_SH1 0x1 +#define EF_SH2 0x2 +#define EF_SH3 0x3 +#define EF_SH_DSP 0x4 +#define EF_SH3_DSP 0x5 +#define EF_SH4AL_DSP 0x6 +#define EF_SH3E 0x8 +#define EF_SH4 0x9 +#define EF_SH2E 0xb +#define EF_SH4A 0xc +#define EF_SH2A 0xd +#define EF_SH4_NOFPU 0x10 +#define EF_SH4A_NOFPU 0x11 +#define EF_SH4_NOMMU_NOFPU 0x12 +#define EF_SH2A_NOFPU 0x13 +#define EF_SH3_NOMMU 0x14 +#define EF_SH2A_SH4_NOFPU 0x15 +#define EF_SH2A_SH3_NOFPU 0x16 +#define EF_SH2A_SH4 0x17 +#define EF_SH2A_SH3E 0x18 + +/* SH relocs. */ +#define R_SH_NONE 0 +#define R_SH_DIR32 1 +#define R_SH_REL32 2 +#define R_SH_DIR8WPN 3 +#define R_SH_IND12W 4 +#define R_SH_DIR8WPL 5 +#define R_SH_DIR8WPZ 6 +#define R_SH_DIR8BP 7 +#define R_SH_DIR8W 8 +#define R_SH_DIR8L 9 +#define R_SH_SWITCH16 25 +#define R_SH_SWITCH32 26 +#define R_SH_USES 27 +#define R_SH_COUNT 28 +#define R_SH_ALIGN 29 +#define R_SH_CODE 30 +#define R_SH_DATA 31 +#define R_SH_LABEL 32 +#define R_SH_SWITCH8 33 +#define R_SH_GNU_VTINHERIT 34 +#define R_SH_GNU_VTENTRY 35 +#define R_SH_TLS_GD_32 144 +#define R_SH_TLS_LD_32 145 +#define R_SH_TLS_LDO_32 146 +#define R_SH_TLS_IE_32 147 +#define R_SH_TLS_LE_32 148 +#define R_SH_TLS_DTPMOD32 149 +#define R_SH_TLS_DTPOFF32 150 +#define R_SH_TLS_TPOFF32 151 +#define R_SH_GOT32 160 +#define R_SH_PLT32 161 +#define R_SH_COPY 162 +#define R_SH_GLOB_DAT 163 +#define R_SH_JMP_SLOT 164 +#define R_SH_RELATIVE 165 +#define R_SH_GOTOFF 166 +#define R_SH_GOTPC 167 +/* Keep this the last entry. */ +#define R_SH_NUM 256 + +/* S/390 specific definitions. */ + +/* Valid values for the e_flags field. */ + +#define EF_S390_HIGH_GPRS 0x00000001 /* High GPRs kernel facility needed. */ + +/* Additional s390 relocs */ + +#define R_390_NONE 0 /* No reloc. */ +#define R_390_8 1 /* Direct 8 bit. */ +#define R_390_12 2 /* Direct 12 bit. */ +#define R_390_16 3 /* Direct 16 bit. */ +#define R_390_32 4 /* Direct 32 bit. */ +#define R_390_PC32 5 /* PC relative 32 bit. */ +#define R_390_GOT12 6 /* 12 bit GOT offset. */ +#define R_390_GOT32 7 /* 32 bit GOT offset. */ +#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */ +#define R_390_COPY 9 /* Copy symbol at runtime. */ +#define R_390_GLOB_DAT 10 /* Create GOT entry. */ +#define R_390_JMP_SLOT 11 /* Create PLT entry. */ +#define R_390_RELATIVE 12 /* Adjust by program base. */ +#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */ +#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */ +#define R_390_GOT16 15 /* 16 bit GOT offset. */ +#define R_390_PC16 16 /* PC relative 16 bit. */ +#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */ +#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */ +#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */ +#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */ +#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */ +#define R_390_64 22 /* Direct 64 bit. */ +#define R_390_PC64 23 /* PC relative 64 bit. */ +#define R_390_GOT64 24 /* 64 bit GOT offset. */ +#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */ +#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */ +#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */ +#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */ +#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */ +#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */ +#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */ +#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */ +#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */ +#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */ +#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */ +#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */ +#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */ +#define R_390_TLS_GDCALL 38 /* Tag for function call in general + dynamic TLS code. */ +#define R_390_TLS_LDCALL 39 /* Tag for function call in local + dynamic TLS code. */ +#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic + thread local data. */ +#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic + thread local data. */ +#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic + thread local data in LE code. */ +#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic + thread local data in LE code. */ +#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to + static TLS block. */ +#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to + static TLS block. */ +#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS + block. */ +#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS + block. */ +#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */ +#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */ +#define R_390_TLS_TPOFF 56 /* Negated offset in static TLS + block. */ +#define R_390_20 57 /* Direct 20 bit. */ +#define R_390_GOT20 58 /* 20 bit GOT offset. */ +#define R_390_GOTPLT20 59 /* 20 bit offset to jump slot. */ +#define R_390_TLS_GOTIE20 60 /* 20 bit GOT offset for static TLS + block offset. */ +#define R_390_IRELATIVE 61 /* STT_GNU_IFUNC relocation. */ +/* Keep this the last entry. */ +#define R_390_NUM 62 + + +/* CRIS relocations. */ +#define R_CRIS_NONE 0 +#define R_CRIS_8 1 +#define R_CRIS_16 2 +#define R_CRIS_32 3 +#define R_CRIS_8_PCREL 4 +#define R_CRIS_16_PCREL 5 +#define R_CRIS_32_PCREL 6 +#define R_CRIS_GNU_VTINHERIT 7 +#define R_CRIS_GNU_VTENTRY 8 +#define R_CRIS_COPY 9 +#define R_CRIS_GLOB_DAT 10 +#define R_CRIS_JUMP_SLOT 11 +#define R_CRIS_RELATIVE 12 +#define R_CRIS_16_GOT 13 +#define R_CRIS_32_GOT 14 +#define R_CRIS_16_GOTPLT 15 +#define R_CRIS_32_GOTPLT 16 +#define R_CRIS_32_GOTREL 17 +#define R_CRIS_32_PLT_GOTREL 18 +#define R_CRIS_32_PLT_PCREL 19 + +#define R_CRIS_NUM 20 + + +/* AMD x86-64 relocations. */ +#define R_X86_64_NONE 0 /* No reloc */ +#define R_X86_64_64 1 /* Direct 64 bit */ +#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ +#define R_X86_64_GOT32 3 /* 32 bit GOT entry */ +#define R_X86_64_PLT32 4 /* 32 bit PLT address */ +#define R_X86_64_COPY 5 /* Copy symbol at runtime */ +#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */ +#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ +#define R_X86_64_RELATIVE 8 /* Adjust by program base */ +#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative + offset to GOT */ +#define R_X86_64_32 10 /* Direct 32 bit zero extended */ +#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ +#define R_X86_64_16 12 /* Direct 16 bit zero extended */ +#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */ +#define R_X86_64_8 14 /* Direct 8 bit sign extended */ +#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */ +#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */ +#define R_X86_64_DTPOFF64 17 /* Offset in module's TLS block */ +#define R_X86_64_TPOFF64 18 /* Offset in initial TLS block */ +#define R_X86_64_TLSGD 19 /* 32 bit signed PC relative offset + to two GOT entries for GD symbol */ +#define R_X86_64_TLSLD 20 /* 32 bit signed PC relative offset + to two GOT entries for LD symbol */ +#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */ +#define R_X86_64_GOTTPOFF 22 /* 32 bit signed PC relative offset + to GOT entry for IE symbol */ +#define R_X86_64_TPOFF32 23 /* Offset in initial TLS block */ +#define R_X86_64_PC64 24 /* PC relative 64 bit */ +#define R_X86_64_GOTOFF64 25 /* 64 bit offset to GOT */ +#define R_X86_64_GOTPC32 26 /* 32 bit signed pc relative + offset to GOT */ +#define R_X86_64_GOT64 27 /* 64-bit GOT entry offset */ +#define R_X86_64_GOTPCREL64 28 /* 64-bit PC relative offset + to GOT entry */ +#define R_X86_64_GOTPC64 29 /* 64-bit PC relative offset to GOT */ +#define R_X86_64_GOTPLT64 30 /* like GOT64, says PLT entry needed */ +#define R_X86_64_PLTOFF64 31 /* 64-bit GOT relative offset + to PLT entry */ +#define R_X86_64_SIZE32 32 /* Size of symbol plus 32-bit addend */ +#define R_X86_64_SIZE64 33 /* Size of symbol plus 64-bit addend */ +#define R_X86_64_GOTPC32_TLSDESC 34 /* GOT offset for TLS descriptor. */ +#define R_X86_64_TLSDESC_CALL 35 /* Marker for call through TLS + descriptor. */ +#define R_X86_64_TLSDESC 36 /* TLS descriptor. */ +#define R_X86_64_IRELATIVE 37 /* Adjust indirectly by program base */ +#define R_X86_64_RELATIVE64 38 /* 64-bit adjust by program base */ + /* 39 Reserved was R_X86_64_PC32_BND */ + /* 40 Reserved was R_X86_64_PLT32_BND */ +#define R_X86_64_GOTPCRELX 41 /* Load from 32 bit signed pc relative + offset to GOT entry without REX + prefix, relaxable. */ +#define R_X86_64_REX_GOTPCRELX 42 /* Load from 32 bit signed pc relative + offset to GOT entry with REX prefix, + relaxable. */ +#define R_X86_64_NUM 43 + +/* x86-64 sh_type values. */ +#define SHT_X86_64_UNWIND 0x70000001 /* Unwind information. */ + +/* x86-64 d_tag values. */ +#define DT_X86_64_PLT (DT_LOPROC + 0) +#define DT_X86_64_PLTSZ (DT_LOPROC + 1) +#define DT_X86_64_PLTENT (DT_LOPROC + 3) +#define DT_X86_64_NUM 4 + +/* AM33 relocations. */ +#define R_MN10300_NONE 0 /* No reloc. */ +#define R_MN10300_32 1 /* Direct 32 bit. */ +#define R_MN10300_16 2 /* Direct 16 bit. */ +#define R_MN10300_8 3 /* Direct 8 bit. */ +#define R_MN10300_PCREL32 4 /* PC-relative 32-bit. */ +#define R_MN10300_PCREL16 5 /* PC-relative 16-bit signed. */ +#define R_MN10300_PCREL8 6 /* PC-relative 8-bit signed. */ +#define R_MN10300_GNU_VTINHERIT 7 /* Ancient C++ vtable garbage... */ +#define R_MN10300_GNU_VTENTRY 8 /* ... collection annotation. */ +#define R_MN10300_24 9 /* Direct 24 bit. */ +#define R_MN10300_GOTPC32 10 /* 32-bit PCrel offset to GOT. */ +#define R_MN10300_GOTPC16 11 /* 16-bit PCrel offset to GOT. */ +#define R_MN10300_GOTOFF32 12 /* 32-bit offset from GOT. */ +#define R_MN10300_GOTOFF24 13 /* 24-bit offset from GOT. */ +#define R_MN10300_GOTOFF16 14 /* 16-bit offset from GOT. */ +#define R_MN10300_PLT32 15 /* 32-bit PCrel to PLT entry. */ +#define R_MN10300_PLT16 16 /* 16-bit PCrel to PLT entry. */ +#define R_MN10300_GOT32 17 /* 32-bit offset to GOT entry. */ +#define R_MN10300_GOT24 18 /* 24-bit offset to GOT entry. */ +#define R_MN10300_GOT16 19 /* 16-bit offset to GOT entry. */ +#define R_MN10300_COPY 20 /* Copy symbol at runtime. */ +#define R_MN10300_GLOB_DAT 21 /* Create GOT entry. */ +#define R_MN10300_JMP_SLOT 22 /* Create PLT entry. */ +#define R_MN10300_RELATIVE 23 /* Adjust by program base. */ +#define R_MN10300_TLS_GD 24 /* 32-bit offset for global dynamic. */ +#define R_MN10300_TLS_LD 25 /* 32-bit offset for local dynamic. */ +#define R_MN10300_TLS_LDO 26 /* Module-relative offset. */ +#define R_MN10300_TLS_GOTIE 27 /* GOT offset for static TLS block + offset. */ +#define R_MN10300_TLS_IE 28 /* GOT address for static TLS block + offset. */ +#define R_MN10300_TLS_LE 29 /* Offset relative to static TLS + block. */ +#define R_MN10300_TLS_DTPMOD 30 /* ID of module containing symbol. */ +#define R_MN10300_TLS_DTPOFF 31 /* Offset in module TLS block. */ +#define R_MN10300_TLS_TPOFF 32 /* Offset in static TLS block. */ +#define R_MN10300_SYM_DIFF 33 /* Adjustment for next reloc as needed + by linker relaxation. */ +#define R_MN10300_ALIGN 34 /* Alignment requirement for linker + relaxation. */ +#define R_MN10300_NUM 35 + + +/* M32R relocs. */ +#define R_M32R_NONE 0 /* No reloc. */ +#define R_M32R_16 1 /* Direct 16 bit. */ +#define R_M32R_32 2 /* Direct 32 bit. */ +#define R_M32R_24 3 /* Direct 24 bit. */ +#define R_M32R_10_PCREL 4 /* PC relative 10 bit shifted. */ +#define R_M32R_18_PCREL 5 /* PC relative 18 bit shifted. */ +#define R_M32R_26_PCREL 6 /* PC relative 26 bit shifted. */ +#define R_M32R_HI16_ULO 7 /* High 16 bit with unsigned low. */ +#define R_M32R_HI16_SLO 8 /* High 16 bit with signed low. */ +#define R_M32R_LO16 9 /* Low 16 bit. */ +#define R_M32R_SDA16 10 /* 16 bit offset in SDA. */ +#define R_M32R_GNU_VTINHERIT 11 +#define R_M32R_GNU_VTENTRY 12 +/* M32R relocs use SHT_RELA. */ +#define R_M32R_16_RELA 33 /* Direct 16 bit. */ +#define R_M32R_32_RELA 34 /* Direct 32 bit. */ +#define R_M32R_24_RELA 35 /* Direct 24 bit. */ +#define R_M32R_10_PCREL_RELA 36 /* PC relative 10 bit shifted. */ +#define R_M32R_18_PCREL_RELA 37 /* PC relative 18 bit shifted. */ +#define R_M32R_26_PCREL_RELA 38 /* PC relative 26 bit shifted. */ +#define R_M32R_HI16_ULO_RELA 39 /* High 16 bit with unsigned low */ +#define R_M32R_HI16_SLO_RELA 40 /* High 16 bit with signed low */ +#define R_M32R_LO16_RELA 41 /* Low 16 bit */ +#define R_M32R_SDA16_RELA 42 /* 16 bit offset in SDA */ +#define R_M32R_RELA_GNU_VTINHERIT 43 +#define R_M32R_RELA_GNU_VTENTRY 44 +#define R_M32R_REL32 45 /* PC relative 32 bit. */ + +#define R_M32R_GOT24 48 /* 24 bit GOT entry */ +#define R_M32R_26_PLTREL 49 /* 26 bit PC relative to PLT shifted */ +#define R_M32R_COPY 50 /* Copy symbol at runtime */ +#define R_M32R_GLOB_DAT 51 /* Create GOT entry */ +#define R_M32R_JMP_SLOT 52 /* Create PLT entry */ +#define R_M32R_RELATIVE 53 /* Adjust by program base */ +#define R_M32R_GOTOFF 54 /* 24 bit offset to GOT */ +#define R_M32R_GOTPC24 55 /* 24 bit PC relative offset to GOT */ +#define R_M32R_GOT16_HI_ULO 56 /* High 16 bit GOT entry with unsigned + low */ +#define R_M32R_GOT16_HI_SLO 57 /* High 16 bit GOT entry with signed + low */ +#define R_M32R_GOT16_LO 58 /* Low 16 bit GOT entry */ +#define R_M32R_GOTPC_HI_ULO 59 /* High 16 bit PC relative offset to + GOT with unsigned low */ +#define R_M32R_GOTPC_HI_SLO 60 /* High 16 bit PC relative offset to + GOT with signed low */ +#define R_M32R_GOTPC_LO 61 /* Low 16 bit PC relative offset to + GOT */ +#define R_M32R_GOTOFF_HI_ULO 62 /* High 16 bit offset to GOT + with unsigned low */ +#define R_M32R_GOTOFF_HI_SLO 63 /* High 16 bit offset to GOT + with signed low */ +#define R_M32R_GOTOFF_LO 64 /* Low 16 bit offset to GOT */ +#define R_M32R_NUM 256 /* Keep this the last entry. */ + +/* MicroBlaze relocations */ +#define R_MICROBLAZE_NONE 0 /* No reloc. */ +#define R_MICROBLAZE_32 1 /* Direct 32 bit. */ +#define R_MICROBLAZE_32_PCREL 2 /* PC relative 32 bit. */ +#define R_MICROBLAZE_64_PCREL 3 /* PC relative 64 bit. */ +#define R_MICROBLAZE_32_PCREL_LO 4 /* Low 16 bits of PCREL32. */ +#define R_MICROBLAZE_64 5 /* Direct 64 bit. */ +#define R_MICROBLAZE_32_LO 6 /* Low 16 bit. */ +#define R_MICROBLAZE_SRO32 7 /* Read-only small data area. */ +#define R_MICROBLAZE_SRW32 8 /* Read-write small data area. */ +#define R_MICROBLAZE_64_NONE 9 /* No reloc. */ +#define R_MICROBLAZE_32_SYM_OP_SYM 10 /* Symbol Op Symbol relocation. */ +#define R_MICROBLAZE_GNU_VTINHERIT 11 /* GNU C++ vtable hierarchy. */ +#define R_MICROBLAZE_GNU_VTENTRY 12 /* GNU C++ vtable member usage. */ +#define R_MICROBLAZE_GOTPC_64 13 /* PC-relative GOT offset. */ +#define R_MICROBLAZE_GOT_64 14 /* GOT entry offset. */ +#define R_MICROBLAZE_PLT_64 15 /* PLT offset (PC-relative). */ +#define R_MICROBLAZE_REL 16 /* Adjust by program base. */ +#define R_MICROBLAZE_JUMP_SLOT 17 /* Create PLT entry. */ +#define R_MICROBLAZE_GLOB_DAT 18 /* Create GOT entry. */ +#define R_MICROBLAZE_GOTOFF_64 19 /* 64 bit offset to GOT. */ +#define R_MICROBLAZE_GOTOFF_32 20 /* 32 bit offset to GOT. */ +#define R_MICROBLAZE_COPY 21 /* Runtime copy. */ +#define R_MICROBLAZE_TLS 22 /* TLS Reloc. */ +#define R_MICROBLAZE_TLSGD 23 /* TLS General Dynamic. */ +#define R_MICROBLAZE_TLSLD 24 /* TLS Local Dynamic. */ +#define R_MICROBLAZE_TLSDTPMOD32 25 /* TLS Module ID. */ +#define R_MICROBLAZE_TLSDTPREL32 26 /* TLS Offset Within TLS Block. */ +#define R_MICROBLAZE_TLSDTPREL64 27 /* TLS Offset Within TLS Block. */ +#define R_MICROBLAZE_TLSGOTTPREL32 28 /* TLS Offset From Thread Pointer. */ +#define R_MICROBLAZE_TLSTPREL32 29 /* TLS Offset From Thread Pointer. */ + +/* Legal values for d_tag (dynamic entry type). */ +#define DT_NIOS2_GP 0x70000002 /* Address of _gp. */ + +/* Nios II relocations. */ +#define R_NIOS2_NONE 0 /* No reloc. */ +#define R_NIOS2_S16 1 /* Direct signed 16 bit. */ +#define R_NIOS2_U16 2 /* Direct unsigned 16 bit. */ +#define R_NIOS2_PCREL16 3 /* PC relative 16 bit. */ +#define R_NIOS2_CALL26 4 /* Direct call. */ +#define R_NIOS2_IMM5 5 /* 5 bit constant expression. */ +#define R_NIOS2_CACHE_OPX 6 /* 5 bit expression, shift 22. */ +#define R_NIOS2_IMM6 7 /* 6 bit constant expression. */ +#define R_NIOS2_IMM8 8 /* 8 bit constant expression. */ +#define R_NIOS2_HI16 9 /* High 16 bit. */ +#define R_NIOS2_LO16 10 /* Low 16 bit. */ +#define R_NIOS2_HIADJ16 11 /* High 16 bit, adjusted. */ +#define R_NIOS2_BFD_RELOC_32 12 /* 32 bit symbol value + addend. */ +#define R_NIOS2_BFD_RELOC_16 13 /* 16 bit symbol value + addend. */ +#define R_NIOS2_BFD_RELOC_8 14 /* 8 bit symbol value + addend. */ +#define R_NIOS2_GPREL 15 /* 16 bit GP pointer offset. */ +#define R_NIOS2_GNU_VTINHERIT 16 /* GNU C++ vtable hierarchy. */ +#define R_NIOS2_GNU_VTENTRY 17 /* GNU C++ vtable member usage. */ +#define R_NIOS2_UJMP 18 /* Unconditional branch. */ +#define R_NIOS2_CJMP 19 /* Conditional branch. */ +#define R_NIOS2_CALLR 20 /* Indirect call through register. */ +#define R_NIOS2_ALIGN 21 /* Alignment requirement for + linker relaxation. */ +#define R_NIOS2_GOT16 22 /* 16 bit GOT entry. */ +#define R_NIOS2_CALL16 23 /* 16 bit GOT entry for function. */ +#define R_NIOS2_GOTOFF_LO 24 /* %lo of offset to GOT pointer. */ +#define R_NIOS2_GOTOFF_HA 25 /* %hiadj of offset to GOT pointer. */ +#define R_NIOS2_PCREL_LO 26 /* %lo of PC relative offset. */ +#define R_NIOS2_PCREL_HA 27 /* %hiadj of PC relative offset. */ +#define R_NIOS2_TLS_GD16 28 /* 16 bit GOT offset for TLS GD. */ +#define R_NIOS2_TLS_LDM16 29 /* 16 bit GOT offset for TLS LDM. */ +#define R_NIOS2_TLS_LDO16 30 /* 16 bit module relative offset. */ +#define R_NIOS2_TLS_IE16 31 /* 16 bit GOT offset for TLS IE. */ +#define R_NIOS2_TLS_LE16 32 /* 16 bit LE TP-relative offset. */ +#define R_NIOS2_TLS_DTPMOD 33 /* Module number. */ +#define R_NIOS2_TLS_DTPREL 34 /* Module-relative offset. */ +#define R_NIOS2_TLS_TPREL 35 /* TP-relative offset. */ +#define R_NIOS2_COPY 36 /* Copy symbol at runtime. */ +#define R_NIOS2_GLOB_DAT 37 /* Create GOT entry. */ +#define R_NIOS2_JUMP_SLOT 38 /* Create PLT entry. */ +#define R_NIOS2_RELATIVE 39 /* Adjust by program base. */ +#define R_NIOS2_GOTOFF 40 /* 16 bit offset to GOT pointer. */ +#define R_NIOS2_CALL26_NOAT 41 /* Direct call in .noat section. */ +#define R_NIOS2_GOT_LO 42 /* %lo() of GOT entry. */ +#define R_NIOS2_GOT_HA 43 /* %hiadj() of GOT entry. */ +#define R_NIOS2_CALL_LO 44 /* %lo() of function GOT entry. */ +#define R_NIOS2_CALL_HA 45 /* %hiadj() of function GOT entry. */ + +/* TILEPro relocations. */ +#define R_TILEPRO_NONE 0 /* No reloc */ +#define R_TILEPRO_32 1 /* Direct 32 bit */ +#define R_TILEPRO_16 2 /* Direct 16 bit */ +#define R_TILEPRO_8 3 /* Direct 8 bit */ +#define R_TILEPRO_32_PCREL 4 /* PC relative 32 bit */ +#define R_TILEPRO_16_PCREL 5 /* PC relative 16 bit */ +#define R_TILEPRO_8_PCREL 6 /* PC relative 8 bit */ +#define R_TILEPRO_LO16 7 /* Low 16 bit */ +#define R_TILEPRO_HI16 8 /* High 16 bit */ +#define R_TILEPRO_HA16 9 /* High 16 bit, adjusted */ +#define R_TILEPRO_COPY 10 /* Copy relocation */ +#define R_TILEPRO_GLOB_DAT 11 /* Create GOT entry */ +#define R_TILEPRO_JMP_SLOT 12 /* Create PLT entry */ +#define R_TILEPRO_RELATIVE 13 /* Adjust by program base */ +#define R_TILEPRO_BROFF_X1 14 /* X1 pipe branch offset */ +#define R_TILEPRO_JOFFLONG_X1 15 /* X1 pipe jump offset */ +#define R_TILEPRO_JOFFLONG_X1_PLT 16 /* X1 pipe jump offset to PLT */ +#define R_TILEPRO_IMM8_X0 17 /* X0 pipe 8-bit */ +#define R_TILEPRO_IMM8_Y0 18 /* Y0 pipe 8-bit */ +#define R_TILEPRO_IMM8_X1 19 /* X1 pipe 8-bit */ +#define R_TILEPRO_IMM8_Y1 20 /* Y1 pipe 8-bit */ +#define R_TILEPRO_MT_IMM15_X1 21 /* X1 pipe mtspr */ +#define R_TILEPRO_MF_IMM15_X1 22 /* X1 pipe mfspr */ +#define R_TILEPRO_IMM16_X0 23 /* X0 pipe 16-bit */ +#define R_TILEPRO_IMM16_X1 24 /* X1 pipe 16-bit */ +#define R_TILEPRO_IMM16_X0_LO 25 /* X0 pipe low 16-bit */ +#define R_TILEPRO_IMM16_X1_LO 26 /* X1 pipe low 16-bit */ +#define R_TILEPRO_IMM16_X0_HI 27 /* X0 pipe high 16-bit */ +#define R_TILEPRO_IMM16_X1_HI 28 /* X1 pipe high 16-bit */ +#define R_TILEPRO_IMM16_X0_HA 29 /* X0 pipe high 16-bit, adjusted */ +#define R_TILEPRO_IMM16_X1_HA 30 /* X1 pipe high 16-bit, adjusted */ +#define R_TILEPRO_IMM16_X0_PCREL 31 /* X0 pipe PC relative 16 bit */ +#define R_TILEPRO_IMM16_X1_PCREL 32 /* X1 pipe PC relative 16 bit */ +#define R_TILEPRO_IMM16_X0_LO_PCREL 33 /* X0 pipe PC relative low 16 bit */ +#define R_TILEPRO_IMM16_X1_LO_PCREL 34 /* X1 pipe PC relative low 16 bit */ +#define R_TILEPRO_IMM16_X0_HI_PCREL 35 /* X0 pipe PC relative high 16 bit */ +#define R_TILEPRO_IMM16_X1_HI_PCREL 36 /* X1 pipe PC relative high 16 bit */ +#define R_TILEPRO_IMM16_X0_HA_PCREL 37 /* X0 pipe PC relative ha() 16 bit */ +#define R_TILEPRO_IMM16_X1_HA_PCREL 38 /* X1 pipe PC relative ha() 16 bit */ +#define R_TILEPRO_IMM16_X0_GOT 39 /* X0 pipe 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X1_GOT 40 /* X1 pipe 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X0_GOT_LO 41 /* X0 pipe low 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X1_GOT_LO 42 /* X1 pipe low 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X0_GOT_HI 43 /* X0 pipe high 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X1_GOT_HI 44 /* X1 pipe high 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X0_GOT_HA 45 /* X0 pipe ha() 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X1_GOT_HA 46 /* X1 pipe ha() 16-bit GOT offset */ +#define R_TILEPRO_MMSTART_X0 47 /* X0 pipe mm "start" */ +#define R_TILEPRO_MMEND_X0 48 /* X0 pipe mm "end" */ +#define R_TILEPRO_MMSTART_X1 49 /* X1 pipe mm "start" */ +#define R_TILEPRO_MMEND_X1 50 /* X1 pipe mm "end" */ +#define R_TILEPRO_SHAMT_X0 51 /* X0 pipe shift amount */ +#define R_TILEPRO_SHAMT_X1 52 /* X1 pipe shift amount */ +#define R_TILEPRO_SHAMT_Y0 53 /* Y0 pipe shift amount */ +#define R_TILEPRO_SHAMT_Y1 54 /* Y1 pipe shift amount */ +#define R_TILEPRO_DEST_IMM8_X1 55 /* X1 pipe destination 8-bit */ +/* Relocs 56-59 are currently not defined. */ +#define R_TILEPRO_TLS_GD_CALL 60 /* "jal" for TLS GD */ +#define R_TILEPRO_IMM8_X0_TLS_GD_ADD 61 /* X0 pipe "addi" for TLS GD */ +#define R_TILEPRO_IMM8_X1_TLS_GD_ADD 62 /* X1 pipe "addi" for TLS GD */ +#define R_TILEPRO_IMM8_Y0_TLS_GD_ADD 63 /* Y0 pipe "addi" for TLS GD */ +#define R_TILEPRO_IMM8_Y1_TLS_GD_ADD 64 /* Y1 pipe "addi" for TLS GD */ +#define R_TILEPRO_TLS_IE_LOAD 65 /* "lw_tls" for TLS IE */ +#define R_TILEPRO_IMM16_X0_TLS_GD 66 /* X0 pipe 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X1_TLS_GD 67 /* X1 pipe 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X0_TLS_GD_LO 68 /* X0 pipe low 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X1_TLS_GD_LO 69 /* X1 pipe low 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X0_TLS_GD_HI 70 /* X0 pipe high 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X1_TLS_GD_HI 71 /* X1 pipe high 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X0_TLS_GD_HA 72 /* X0 pipe ha() 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X1_TLS_GD_HA 73 /* X1 pipe ha() 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X0_TLS_IE 74 /* X0 pipe 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X1_TLS_IE 75 /* X1 pipe 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X0_TLS_IE_LO 76 /* X0 pipe low 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X1_TLS_IE_LO 77 /* X1 pipe low 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X0_TLS_IE_HI 78 /* X0 pipe high 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X1_TLS_IE_HI 79 /* X1 pipe high 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X0_TLS_IE_HA 80 /* X0 pipe ha() 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X1_TLS_IE_HA 81 /* X1 pipe ha() 16-bit TLS IE offset */ +#define R_TILEPRO_TLS_DTPMOD32 82 /* ID of module containing symbol */ +#define R_TILEPRO_TLS_DTPOFF32 83 /* Offset in TLS block */ +#define R_TILEPRO_TLS_TPOFF32 84 /* Offset in static TLS block */ +#define R_TILEPRO_IMM16_X0_TLS_LE 85 /* X0 pipe 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X1_TLS_LE 86 /* X1 pipe 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X0_TLS_LE_LO 87 /* X0 pipe low 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X1_TLS_LE_LO 88 /* X1 pipe low 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X0_TLS_LE_HI 89 /* X0 pipe high 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X1_TLS_LE_HI 90 /* X1 pipe high 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X0_TLS_LE_HA 91 /* X0 pipe ha() 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X1_TLS_LE_HA 92 /* X1 pipe ha() 16-bit TLS LE offset */ + +#define R_TILEPRO_GNU_VTINHERIT 128 /* GNU C++ vtable hierarchy */ +#define R_TILEPRO_GNU_VTENTRY 129 /* GNU C++ vtable member usage */ + +#define R_TILEPRO_NUM 130 + + +/* TILE-Gx relocations. */ +#define R_TILEGX_NONE 0 /* No reloc */ +#define R_TILEGX_64 1 /* Direct 64 bit */ +#define R_TILEGX_32 2 /* Direct 32 bit */ +#define R_TILEGX_16 3 /* Direct 16 bit */ +#define R_TILEGX_8 4 /* Direct 8 bit */ +#define R_TILEGX_64_PCREL 5 /* PC relative 64 bit */ +#define R_TILEGX_32_PCREL 6 /* PC relative 32 bit */ +#define R_TILEGX_16_PCREL 7 /* PC relative 16 bit */ +#define R_TILEGX_8_PCREL 8 /* PC relative 8 bit */ +#define R_TILEGX_HW0 9 /* hword 0 16-bit */ +#define R_TILEGX_HW1 10 /* hword 1 16-bit */ +#define R_TILEGX_HW2 11 /* hword 2 16-bit */ +#define R_TILEGX_HW3 12 /* hword 3 16-bit */ +#define R_TILEGX_HW0_LAST 13 /* last hword 0 16-bit */ +#define R_TILEGX_HW1_LAST 14 /* last hword 1 16-bit */ +#define R_TILEGX_HW2_LAST 15 /* last hword 2 16-bit */ +#define R_TILEGX_COPY 16 /* Copy relocation */ +#define R_TILEGX_GLOB_DAT 17 /* Create GOT entry */ +#define R_TILEGX_JMP_SLOT 18 /* Create PLT entry */ +#define R_TILEGX_RELATIVE 19 /* Adjust by program base */ +#define R_TILEGX_BROFF_X1 20 /* X1 pipe branch offset */ +#define R_TILEGX_JUMPOFF_X1 21 /* X1 pipe jump offset */ +#define R_TILEGX_JUMPOFF_X1_PLT 22 /* X1 pipe jump offset to PLT */ +#define R_TILEGX_IMM8_X0 23 /* X0 pipe 8-bit */ +#define R_TILEGX_IMM8_Y0 24 /* Y0 pipe 8-bit */ +#define R_TILEGX_IMM8_X1 25 /* X1 pipe 8-bit */ +#define R_TILEGX_IMM8_Y1 26 /* Y1 pipe 8-bit */ +#define R_TILEGX_DEST_IMM8_X1 27 /* X1 pipe destination 8-bit */ +#define R_TILEGX_MT_IMM14_X1 28 /* X1 pipe mtspr */ +#define R_TILEGX_MF_IMM14_X1 29 /* X1 pipe mfspr */ +#define R_TILEGX_MMSTART_X0 30 /* X0 pipe mm "start" */ +#define R_TILEGX_MMEND_X0 31 /* X0 pipe mm "end" */ +#define R_TILEGX_SHAMT_X0 32 /* X0 pipe shift amount */ +#define R_TILEGX_SHAMT_X1 33 /* X1 pipe shift amount */ +#define R_TILEGX_SHAMT_Y0 34 /* Y0 pipe shift amount */ +#define R_TILEGX_SHAMT_Y1 35 /* Y1 pipe shift amount */ +#define R_TILEGX_IMM16_X0_HW0 36 /* X0 pipe hword 0 */ +#define R_TILEGX_IMM16_X1_HW0 37 /* X1 pipe hword 0 */ +#define R_TILEGX_IMM16_X0_HW1 38 /* X0 pipe hword 1 */ +#define R_TILEGX_IMM16_X1_HW1 39 /* X1 pipe hword 1 */ +#define R_TILEGX_IMM16_X0_HW2 40 /* X0 pipe hword 2 */ +#define R_TILEGX_IMM16_X1_HW2 41 /* X1 pipe hword 2 */ +#define R_TILEGX_IMM16_X0_HW3 42 /* X0 pipe hword 3 */ +#define R_TILEGX_IMM16_X1_HW3 43 /* X1 pipe hword 3 */ +#define R_TILEGX_IMM16_X0_HW0_LAST 44 /* X0 pipe last hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_LAST 45 /* X1 pipe last hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_LAST 46 /* X0 pipe last hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_LAST 47 /* X1 pipe last hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_LAST 48 /* X0 pipe last hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_LAST 49 /* X1 pipe last hword 2 */ +#define R_TILEGX_IMM16_X0_HW0_PCREL 50 /* X0 pipe PC relative hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_PCREL 51 /* X1 pipe PC relative hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_PCREL 52 /* X0 pipe PC relative hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_PCREL 53 /* X1 pipe PC relative hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_PCREL 54 /* X0 pipe PC relative hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_PCREL 55 /* X1 pipe PC relative hword 2 */ +#define R_TILEGX_IMM16_X0_HW3_PCREL 56 /* X0 pipe PC relative hword 3 */ +#define R_TILEGX_IMM16_X1_HW3_PCREL 57 /* X1 pipe PC relative hword 3 */ +#define R_TILEGX_IMM16_X0_HW0_LAST_PCREL 58 /* X0 pipe PC-rel last hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_LAST_PCREL 59 /* X1 pipe PC-rel last hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_LAST_PCREL 60 /* X0 pipe PC-rel last hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_LAST_PCREL 61 /* X1 pipe PC-rel last hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_LAST_PCREL 62 /* X0 pipe PC-rel last hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_LAST_PCREL 63 /* X1 pipe PC-rel last hword 2 */ +#define R_TILEGX_IMM16_X0_HW0_GOT 64 /* X0 pipe hword 0 GOT offset */ +#define R_TILEGX_IMM16_X1_HW0_GOT 65 /* X1 pipe hword 0 GOT offset */ +#define R_TILEGX_IMM16_X0_HW0_PLT_PCREL 66 /* X0 pipe PC-rel PLT hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_PLT_PCREL 67 /* X1 pipe PC-rel PLT hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_PLT_PCREL 68 /* X0 pipe PC-rel PLT hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_PLT_PCREL 69 /* X1 pipe PC-rel PLT hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_PLT_PCREL 70 /* X0 pipe PC-rel PLT hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_PLT_PCREL 71 /* X1 pipe PC-rel PLT hword 2 */ +#define R_TILEGX_IMM16_X0_HW0_LAST_GOT 72 /* X0 pipe last hword 0 GOT offset */ +#define R_TILEGX_IMM16_X1_HW0_LAST_GOT 73 /* X1 pipe last hword 0 GOT offset */ +#define R_TILEGX_IMM16_X0_HW1_LAST_GOT 74 /* X0 pipe last hword 1 GOT offset */ +#define R_TILEGX_IMM16_X1_HW1_LAST_GOT 75 /* X1 pipe last hword 1 GOT offset */ +#define R_TILEGX_IMM16_X0_HW3_PLT_PCREL 76 /* X0 pipe PC-rel PLT hword 3 */ +#define R_TILEGX_IMM16_X1_HW3_PLT_PCREL 77 /* X1 pipe PC-rel PLT hword 3 */ +#define R_TILEGX_IMM16_X0_HW0_TLS_GD 78 /* X0 pipe hword 0 TLS GD offset */ +#define R_TILEGX_IMM16_X1_HW0_TLS_GD 79 /* X1 pipe hword 0 TLS GD offset */ +#define R_TILEGX_IMM16_X0_HW0_TLS_LE 80 /* X0 pipe hword 0 TLS LE offset */ +#define R_TILEGX_IMM16_X1_HW0_TLS_LE 81 /* X1 pipe hword 0 TLS LE offset */ +#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_LE 82 /* X0 pipe last hword 0 LE off */ +#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_LE 83 /* X1 pipe last hword 0 LE off */ +#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_LE 84 /* X0 pipe last hword 1 LE off */ +#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_LE 85 /* X1 pipe last hword 1 LE off */ +#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_GD 86 /* X0 pipe last hword 0 GD off */ +#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_GD 87 /* X1 pipe last hword 0 GD off */ +#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_GD 88 /* X0 pipe last hword 1 GD off */ +#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_GD 89 /* X1 pipe last hword 1 GD off */ +/* Relocs 90-91 are currently not defined. */ +#define R_TILEGX_IMM16_X0_HW0_TLS_IE 92 /* X0 pipe hword 0 TLS IE offset */ +#define R_TILEGX_IMM16_X1_HW0_TLS_IE 93 /* X1 pipe hword 0 TLS IE offset */ +#define R_TILEGX_IMM16_X0_HW0_LAST_PLT_PCREL 94 /* X0 pipe PC-rel PLT last hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_LAST_PLT_PCREL 95 /* X1 pipe PC-rel PLT last hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_LAST_PLT_PCREL 96 /* X0 pipe PC-rel PLT last hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_LAST_PLT_PCREL 97 /* X1 pipe PC-rel PLT last hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_LAST_PLT_PCREL 98 /* X0 pipe PC-rel PLT last hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_LAST_PLT_PCREL 99 /* X1 pipe PC-rel PLT last hword 2 */ +#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_IE 100 /* X0 pipe last hword 0 IE off */ +#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_IE 101 /* X1 pipe last hword 0 IE off */ +#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_IE 102 /* X0 pipe last hword 1 IE off */ +#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_IE 103 /* X1 pipe last hword 1 IE off */ +/* Relocs 104-105 are currently not defined. */ +#define R_TILEGX_TLS_DTPMOD64 106 /* 64-bit ID of symbol's module */ +#define R_TILEGX_TLS_DTPOFF64 107 /* 64-bit offset in TLS block */ +#define R_TILEGX_TLS_TPOFF64 108 /* 64-bit offset in static TLS block */ +#define R_TILEGX_TLS_DTPMOD32 109 /* 32-bit ID of symbol's module */ +#define R_TILEGX_TLS_DTPOFF32 110 /* 32-bit offset in TLS block */ +#define R_TILEGX_TLS_TPOFF32 111 /* 32-bit offset in static TLS block */ +#define R_TILEGX_TLS_GD_CALL 112 /* "jal" for TLS GD */ +#define R_TILEGX_IMM8_X0_TLS_GD_ADD 113 /* X0 pipe "addi" for TLS GD */ +#define R_TILEGX_IMM8_X1_TLS_GD_ADD 114 /* X1 pipe "addi" for TLS GD */ +#define R_TILEGX_IMM8_Y0_TLS_GD_ADD 115 /* Y0 pipe "addi" for TLS GD */ +#define R_TILEGX_IMM8_Y1_TLS_GD_ADD 116 /* Y1 pipe "addi" for TLS GD */ +#define R_TILEGX_TLS_IE_LOAD 117 /* "ld_tls" for TLS IE */ +#define R_TILEGX_IMM8_X0_TLS_ADD 118 /* X0 pipe "addi" for TLS GD/IE */ +#define R_TILEGX_IMM8_X1_TLS_ADD 119 /* X1 pipe "addi" for TLS GD/IE */ +#define R_TILEGX_IMM8_Y0_TLS_ADD 120 /* Y0 pipe "addi" for TLS GD/IE */ +#define R_TILEGX_IMM8_Y1_TLS_ADD 121 /* Y1 pipe "addi" for TLS GD/IE */ + +#define R_TILEGX_GNU_VTINHERIT 128 /* GNU C++ vtable hierarchy */ +#define R_TILEGX_GNU_VTENTRY 129 /* GNU C++ vtable member usage */ + +#define R_TILEGX_NUM 130 + +/* RISC-V ELF Flags */ +#define EF_RISCV_RVC 0x0001 +#define EF_RISCV_FLOAT_ABI 0x0006 +#define EF_RISCV_FLOAT_ABI_SOFT 0x0000 +#define EF_RISCV_FLOAT_ABI_SINGLE 0x0002 +#define EF_RISCV_FLOAT_ABI_DOUBLE 0x0004 +#define EF_RISCV_FLOAT_ABI_QUAD 0x0006 +#define EF_RISCV_RVE 0x0008 +#define EF_RISCV_TSO 0x0010 + +/* RISC-V relocations. */ +#define R_RISCV_NONE 0 +#define R_RISCV_32 1 +#define R_RISCV_64 2 +#define R_RISCV_RELATIVE 3 +#define R_RISCV_COPY 4 +#define R_RISCV_JUMP_SLOT 5 +#define R_RISCV_TLS_DTPMOD32 6 +#define R_RISCV_TLS_DTPMOD64 7 +#define R_RISCV_TLS_DTPREL32 8 +#define R_RISCV_TLS_DTPREL64 9 +#define R_RISCV_TLS_TPREL32 10 +#define R_RISCV_TLS_TPREL64 11 +#define R_RISCV_BRANCH 16 +#define R_RISCV_JAL 17 +#define R_RISCV_CALL 18 +#define R_RISCV_CALL_PLT 19 +#define R_RISCV_GOT_HI20 20 +#define R_RISCV_TLS_GOT_HI20 21 +#define R_RISCV_TLS_GD_HI20 22 +#define R_RISCV_PCREL_HI20 23 +#define R_RISCV_PCREL_LO12_I 24 +#define R_RISCV_PCREL_LO12_S 25 +#define R_RISCV_HI20 26 +#define R_RISCV_LO12_I 27 +#define R_RISCV_LO12_S 28 +#define R_RISCV_TPREL_HI20 29 +#define R_RISCV_TPREL_LO12_I 30 +#define R_RISCV_TPREL_LO12_S 31 +#define R_RISCV_TPREL_ADD 32 +#define R_RISCV_ADD8 33 +#define R_RISCV_ADD16 34 +#define R_RISCV_ADD32 35 +#define R_RISCV_ADD64 36 +#define R_RISCV_SUB8 37 +#define R_RISCV_SUB16 38 +#define R_RISCV_SUB32 39 +#define R_RISCV_SUB64 40 +#define R_RISCV_GNU_VTINHERIT 41 +#define R_RISCV_GNU_VTENTRY 42 +#define R_RISCV_ALIGN 43 +#define R_RISCV_RVC_BRANCH 44 +#define R_RISCV_RVC_JUMP 45 +#define R_RISCV_RVC_LUI 46 +#define R_RISCV_GPREL_I 47 +#define R_RISCV_GPREL_S 48 +#define R_RISCV_TPREL_I 49 +#define R_RISCV_TPREL_S 50 +#define R_RISCV_RELAX 51 +#define R_RISCV_SUB6 52 +#define R_RISCV_SET6 53 +#define R_RISCV_SET8 54 +#define R_RISCV_SET16 55 +#define R_RISCV_SET32 56 +#define R_RISCV_32_PCREL 57 +#define R_RISCV_IRELATIVE 58 +#define R_RISCV_PLT32 59 +#define R_RISCV_SET_ULEB128 60 +#define R_RISCV_SUB_ULEB128 61 + +#define R_RISCV_NUM 62 + +/* RISC-V specific values for the st_other field. */ +#define STO_RISCV_VARIANT_CC 0x80 /* Function uses variant calling + convention */ + +/* RISC-V specific values for the sh_type field. */ +#define SHT_RISCV_ATTRIBUTES (SHT_LOPROC + 3) + +/* RISC-V specific values for the p_type field. */ +#define PT_RISCV_ATTRIBUTES (PT_LOPROC + 3) + +/* RISC-V specific values for the d_tag field. */ +#define DT_RISCV_VARIANT_CC (DT_LOPROC + 1) + +/* BPF specific declarations. */ + +#define R_BPF_NONE 0 /* No reloc */ +#define R_BPF_64_64 1 +#define R_BPF_64_32 10 + +/* Imagination Meta specific relocations. */ + +#define R_METAG_HIADDR16 0 +#define R_METAG_LOADDR16 1 +#define R_METAG_ADDR32 2 /* 32bit absolute address */ +#define R_METAG_NONE 3 /* No reloc */ +#define R_METAG_RELBRANCH 4 +#define R_METAG_GETSETOFF 5 + +/* Backward compatibility */ +#define R_METAG_REG32OP1 6 +#define R_METAG_REG32OP2 7 +#define R_METAG_REG32OP3 8 +#define R_METAG_REG16OP1 9 +#define R_METAG_REG16OP2 10 +#define R_METAG_REG16OP3 11 +#define R_METAG_REG32OP4 12 + +#define R_METAG_HIOG 13 +#define R_METAG_LOOG 14 + +#define R_METAG_REL8 15 +#define R_METAG_REL16 16 + +/* GNU */ +#define R_METAG_GNU_VTINHERIT 30 +#define R_METAG_GNU_VTENTRY 31 + +/* PIC relocations */ +#define R_METAG_HI16_GOTOFF 32 +#define R_METAG_LO16_GOTOFF 33 +#define R_METAG_GETSET_GOTOFF 34 +#define R_METAG_GETSET_GOT 35 +#define R_METAG_HI16_GOTPC 36 +#define R_METAG_LO16_GOTPC 37 +#define R_METAG_HI16_PLT 38 +#define R_METAG_LO16_PLT 39 +#define R_METAG_RELBRANCH_PLT 40 +#define R_METAG_GOTOFF 41 +#define R_METAG_PLT 42 +#define R_METAG_COPY 43 +#define R_METAG_JMP_SLOT 44 +#define R_METAG_RELATIVE 45 +#define R_METAG_GLOB_DAT 46 + +/* TLS relocations */ +#define R_METAG_TLS_GD 47 +#define R_METAG_TLS_LDM 48 +#define R_METAG_TLS_LDO_HI16 49 +#define R_METAG_TLS_LDO_LO16 50 +#define R_METAG_TLS_LDO 51 +#define R_METAG_TLS_IE 52 +#define R_METAG_TLS_IENONPIC 53 +#define R_METAG_TLS_IENONPIC_HI16 54 +#define R_METAG_TLS_IENONPIC_LO16 55 +#define R_METAG_TLS_TPOFF 56 +#define R_METAG_TLS_DTPMOD 57 +#define R_METAG_TLS_DTPOFF 58 +#define R_METAG_TLS_LE 59 +#define R_METAG_TLS_LE_HI16 60 +#define R_METAG_TLS_LE_LO16 61 + +/* NDS32 relocations. */ +#define R_NDS32_NONE 0 +#define R_NDS32_32_RELA 20 +#define R_NDS32_COPY 39 +#define R_NDS32_GLOB_DAT 40 +#define R_NDS32_JMP_SLOT 41 +#define R_NDS32_RELATIVE 42 +#define R_NDS32_TLS_TPOFF 102 +#define R_NDS32_TLS_DESC 119 + +/* LoongArch ELF Flags */ +#define EF_LARCH_ABI_MODIFIER_MASK 0x07 +#define EF_LARCH_ABI_SOFT_FLOAT 0x01 +#define EF_LARCH_ABI_SINGLE_FLOAT 0x02 +#define EF_LARCH_ABI_DOUBLE_FLOAT 0x03 +#define EF_LARCH_OBJABI_V1 0x40 + +/* LoongArch specific dynamic relocations */ +#define R_LARCH_NONE 0 +#define R_LARCH_32 1 +#define R_LARCH_64 2 +#define R_LARCH_RELATIVE 3 +#define R_LARCH_COPY 4 +#define R_LARCH_JUMP_SLOT 5 +#define R_LARCH_TLS_DTPMOD32 6 +#define R_LARCH_TLS_DTPMOD64 7 +#define R_LARCH_TLS_DTPREL32 8 +#define R_LARCH_TLS_DTPREL64 9 +#define R_LARCH_TLS_TPREL32 10 +#define R_LARCH_TLS_TPREL64 11 +#define R_LARCH_IRELATIVE 12 +#define R_LARCH_TLS_DESC32 13 +#define R_LARCH_TLS_DESC64 14 + +/* Reserved for future relocs that the dynamic linker must understand. */ + +/* used by the static linker for relocating .text. */ +#define R_LARCH_MARK_LA 20 +#define R_LARCH_MARK_PCREL 21 +#define R_LARCH_SOP_PUSH_PCREL 22 +#define R_LARCH_SOP_PUSH_ABSOLUTE 23 +#define R_LARCH_SOP_PUSH_DUP 24 +#define R_LARCH_SOP_PUSH_GPREL 25 +#define R_LARCH_SOP_PUSH_TLS_TPREL 26 +#define R_LARCH_SOP_PUSH_TLS_GOT 27 +#define R_LARCH_SOP_PUSH_TLS_GD 28 +#define R_LARCH_SOP_PUSH_PLT_PCREL 29 +#define R_LARCH_SOP_ASSERT 30 +#define R_LARCH_SOP_NOT 31 +#define R_LARCH_SOP_SUB 32 +#define R_LARCH_SOP_SL 33 +#define R_LARCH_SOP_SR 34 +#define R_LARCH_SOP_ADD 35 +#define R_LARCH_SOP_AND 36 +#define R_LARCH_SOP_IF_ELSE 37 +#define R_LARCH_SOP_POP_32_S_10_5 38 +#define R_LARCH_SOP_POP_32_U_10_12 39 +#define R_LARCH_SOP_POP_32_S_10_12 40 +#define R_LARCH_SOP_POP_32_S_10_16 41 +#define R_LARCH_SOP_POP_32_S_10_16_S2 42 +#define R_LARCH_SOP_POP_32_S_5_20 43 +#define R_LARCH_SOP_POP_32_S_0_5_10_16_S2 44 +#define R_LARCH_SOP_POP_32_S_0_10_10_16_S2 45 +#define R_LARCH_SOP_POP_32_U 46 + +/* used by the static linker for relocating non .text. */ +#define R_LARCH_ADD8 47 +#define R_LARCH_ADD16 48 +#define R_LARCH_ADD24 49 +#define R_LARCH_ADD32 50 +#define R_LARCH_ADD64 51 +#define R_LARCH_SUB8 52 +#define R_LARCH_SUB16 53 +#define R_LARCH_SUB24 54 +#define R_LARCH_SUB32 55 +#define R_LARCH_SUB64 56 +#define R_LARCH_GNU_VTINHERIT 57 +#define R_LARCH_GNU_VTENTRY 58 + +/* reserved 59-63 */ + +#define R_LARCH_B16 64 +#define R_LARCH_B21 65 +#define R_LARCH_B26 66 +#define R_LARCH_ABS_HI20 67 +#define R_LARCH_ABS_LO12 68 +#define R_LARCH_ABS64_LO20 69 +#define R_LARCH_ABS64_HI12 70 +#define R_LARCH_PCALA_HI20 71 +#define R_LARCH_PCALA_LO12 72 +#define R_LARCH_PCALA64_LO20 73 +#define R_LARCH_PCALA64_HI12 74 +#define R_LARCH_GOT_PC_HI20 75 +#define R_LARCH_GOT_PC_LO12 76 +#define R_LARCH_GOT64_PC_LO20 77 +#define R_LARCH_GOT64_PC_HI12 78 +#define R_LARCH_GOT_HI20 79 +#define R_LARCH_GOT_LO12 80 +#define R_LARCH_GOT64_LO20 81 +#define R_LARCH_GOT64_HI12 82 +#define R_LARCH_TLS_LE_HI20 83 +#define R_LARCH_TLS_LE_LO12 84 +#define R_LARCH_TLS_LE64_LO20 85 +#define R_LARCH_TLS_LE64_HI12 86 +#define R_LARCH_TLS_IE_PC_HI20 87 +#define R_LARCH_TLS_IE_PC_LO12 88 +#define R_LARCH_TLS_IE64_PC_LO20 89 +#define R_LARCH_TLS_IE64_PC_HI12 90 +#define R_LARCH_TLS_IE_HI20 91 +#define R_LARCH_TLS_IE_LO12 92 +#define R_LARCH_TLS_IE64_LO20 93 +#define R_LARCH_TLS_IE64_HI12 94 +#define R_LARCH_TLS_LD_PC_HI20 95 +#define R_LARCH_TLS_LD_HI20 96 +#define R_LARCH_TLS_GD_PC_HI20 97 +#define R_LARCH_TLS_GD_HI20 98 +#define R_LARCH_32_PCREL 99 +#define R_LARCH_RELAX 100 +#define R_LARCH_DELETE 101 +#define R_LARCH_ALIGN 102 +#define R_LARCH_PCREL20_S2 103 +#define R_LARCH_CFA 104 +#define R_LARCH_ADD6 105 +#define R_LARCH_SUB6 106 +#define R_LARCH_ADD_ULEB128 107 +#define R_LARCH_SUB_ULEB128 108 +#define R_LARCH_64_PCREL 109 +#define R_LARCH_CALL36 110 +#define R_LARCH_TLS_DESC_PC_HI20 111 +#define R_LARCH_TLS_DESC_PC_LO12 112 +#define R_LARCH_TLS_DESC64_PC_LO20 113 +#define R_LARCH_TLS_DESC64_PC_HI12 114 +#define R_LARCH_TLS_DESC_HI20 115 +#define R_LARCH_TLS_DESC_LO12 116 +#define R_LARCH_TLS_DESC64_LO20 117 +#define R_LARCH_TLS_DESC64_HI12 118 +#define R_LARCH_TLS_DESC_LD 119 +#define R_LARCH_TLS_DESC_CALL 120 +#define R_LARCH_TLS_LE_HI20_R 121 +#define R_LARCH_TLS_LE_ADD_R 122 +#define R_LARCH_TLS_LE_LO12_R 123 +#define R_LARCH_TLS_LD_PCREL20_S2 124 +#define R_LARCH_TLS_GD_PCREL20_S2 125 +#define R_LARCH_TLS_DESC_PCREL20_S2 126 + +/* ARC specific declarations. */ + +/* Processor specific flags for the Ehdr e_flags field. */ +#define EF_ARC_MACH_MSK 0x000000ff +#define EF_ARC_OSABI_MSK 0x00000f00 +#define EF_ARC_ALL_MSK (EF_ARC_MACH_MSK | EF_ARC_OSABI_MSK) + +/* Processor specific values for the Shdr sh_type field. */ +#define SHT_ARC_ATTRIBUTES (SHT_LOPROC + 1) /* ARC attributes section. */ + +/* ARCompact/ARCv2 specific relocs. */ +#define R_ARC_NONE 0x0 +#define R_ARC_8 0x1 +#define R_ARC_16 0x2 +#define R_ARC_24 0x3 +#define R_ARC_32 0x4 + +#define R_ARC_B22_PCREL 0x6 +#define R_ARC_H30 0x7 +#define R_ARC_N8 0x8 +#define R_ARC_N16 0x9 +#define R_ARC_N24 0xA +#define R_ARC_N32 0xB +#define R_ARC_SDA 0xC +#define R_ARC_SECTOFF 0xD +#define R_ARC_S21H_PCREL 0xE +#define R_ARC_S21W_PCREL 0xF +#define R_ARC_S25H_PCREL 0x10 +#define R_ARC_S25W_PCREL 0x11 +#define R_ARC_SDA32 0x12 +#define R_ARC_SDA_LDST 0x13 +#define R_ARC_SDA_LDST1 0x14 +#define R_ARC_SDA_LDST2 0x15 +#define R_ARC_SDA16_LD 0x16 +#define R_ARC_SDA16_LD1 0x17 +#define R_ARC_SDA16_LD2 0x18 +#define R_ARC_S13_PCREL 0x19 +#define R_ARC_W 0x1A +#define R_ARC_32_ME 0x1B +#define R_ARC_N32_ME 0x1C +#define R_ARC_SECTOFF_ME 0x1D +#define R_ARC_SDA32_ME 0x1E +#define R_ARC_W_ME 0x1F +#define R_ARC_H30_ME 0x20 +#define R_ARC_SECTOFF_U8 0x21 +#define R_ARC_SECTOFF_S9 0x22 +#define R_AC_SECTOFF_U8 0x23 +#define R_AC_SECTOFF_U8_1 0x24 +#define R_AC_SECTOFF_U8_2 0x25 +#define R_AC_SECTOFF_S9 0x26 +#define R_AC_SECTOFF_S9_1 0x27 +#define R_AC_SECTOFF_S9_2 0x28 +#define R_ARC_SECTOFF_ME_1 0x29 +#define R_ARC_SECTOFF_ME_2 0x2A +#define R_ARC_SECTOFF_1 0x2B +#define R_ARC_SECTOFF_2 0x2C +#define R_ARC_SDA_12 0x2D +#define R_ARC_SDA16_ST2 0x30 +#define R_ARC_32_PCREL 0x31 +#define R_ARC_PC32 0x32 +#define R_ARC_GOTPC32 0x33 +#define R_ARC_PLT32 0x34 +#define R_ARC_COPY 0x35 +#define R_ARC_GLOB_DAT 0x36 +#define R_ARC_JMP_SLOT 0x37 +#define R_ARC_RELATIVE 0x38 +#define R_ARC_GOTOFF 0x39 +#define R_ARC_GOTPC 0x3A +#define R_ARC_GOT32 0x3B +#define R_ARC_S21W_PCREL_PLT 0x3C +#define R_ARC_S25H_PCREL_PLT 0x3D + +#define R_ARC_JLI_SECTOFF 0x3F + +#define R_ARC_TLS_DTPMOD 0x42 +#define R_ARC_TLS_DTPOFF 0x43 +#define R_ARC_TLS_TPOFF 0x44 +#define R_ARC_TLS_GD_GOT 0x45 +#define R_ARC_TLS_GD_LD 0x46 +#define R_ARC_TLS_GD_CALL 0x47 +#define R_ARC_TLS_IE_GOT 0x48 +#define R_ARC_TLS_DTPOFF_S9 0x49 +#define R_ARC_TLS_LE_S9 0x4A +#define R_ARC_TLS_LE_32 0x4B +#define R_ARC_S25W_PCREL_PLT 0x4C +#define R_ARC_S21H_PCREL_PLT 0x4D +#define R_ARC_NPS_CMEM16 0x4E + +/* OpenRISC 1000 specific relocs. */ +#define R_OR1K_NONE 0 +#define R_OR1K_32 1 +#define R_OR1K_16 2 +#define R_OR1K_8 3 +#define R_OR1K_LO_16_IN_INSN 4 +#define R_OR1K_HI_16_IN_INSN 5 +#define R_OR1K_INSN_REL_26 6 +#define R_OR1K_GNU_VTENTRY 7 +#define R_OR1K_GNU_VTINHERIT 8 +#define R_OR1K_32_PCREL 9 +#define R_OR1K_16_PCREL 10 +#define R_OR1K_8_PCREL 11 +#define R_OR1K_GOTPC_HI16 12 +#define R_OR1K_GOTPC_LO16 13 +#define R_OR1K_GOT16 14 +#define R_OR1K_PLT26 15 +#define R_OR1K_GOTOFF_HI16 16 +#define R_OR1K_GOTOFF_LO16 17 +#define R_OR1K_COPY 18 +#define R_OR1K_GLOB_DAT 19 +#define R_OR1K_JMP_SLOT 20 +#define R_OR1K_RELATIVE 21 +#define R_OR1K_TLS_GD_HI16 22 +#define R_OR1K_TLS_GD_LO16 23 +#define R_OR1K_TLS_LDM_HI16 24 +#define R_OR1K_TLS_LDM_LO16 25 +#define R_OR1K_TLS_LDO_HI16 26 +#define R_OR1K_TLS_LDO_LO16 27 +#define R_OR1K_TLS_IE_HI16 28 +#define R_OR1K_TLS_IE_LO16 29 +#define R_OR1K_TLS_LE_HI16 30 +#define R_OR1K_TLS_LE_LO16 31 +#define R_OR1K_TLS_TPOFF 32 +#define R_OR1K_TLS_DTPOFF 33 +#define R_OR1K_TLS_DTPMOD 34 + +#endif /* elf.h */ diff --git a/scripts/include/endian.h b/scripts/include/endian.h new file mode 100644 index 000000000000..460e21c08fe0 --- /dev/null +++ b/scripts/include/endian.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _TOOLS_ENDIAN_H_2 +#define _TOOLS_ENDIAN_H_2 + +#include + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +#ifndef htole16 +#define htole16(x) (x) +#endif +#ifndef htole32 +#define htole32(x) (x) +#endif +#ifndef htole64 +#define htole64(x) (x) +#endif + +#ifndef le16toh +#define le16toh(x) (x) +#endif + +#ifndef le32toh +#define le32toh(x) (x) +#endif + +#ifndef le64toh +#define le64toh(x) (x) +#endif + +#else /* __BYTE_ORDER */ + +#ifndef htole16 +#define htole16(x) __bswap_16(x) +#endif +#ifndef htole32 +#define htole32(x) __bswap_32(x) +#endif +#ifndef htole64 +#define htole64(x) __bswap_64(x) +#endif + +#ifndef le16toh +#define le16toh(x) __bswap_16(x) +#endif + +#ifndef le32toh +#define le32toh(x) __bswap_32(x) +#endif + +#ifndef le64toh +#define le64toh(x) __bswap_64(x) +#endif + +#endif + +#endif /* _TOOLS_ENDIAN_H_2 */ From f62891f08b8db02260fc1aacb40039f0ecdf8522 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Thu, 27 Nov 2025 10:05:46 +0800 Subject: [PATCH 004/110] file: Call security_file_alloc() after initializing the filp When developing a dedicated LSM module, we need to operate on the file object within the LSM function, such as retrieving the path. However, in `security_file_alloc()`, the passed-in `filp` is only a valid pointer; the content of `filp` is completely uninitialized and entirely random, which confuses the LSM function. Therefore, it is necessary to call `security_file_alloc()` only after the main fields of the `filp` object have been initialized. This patch only moves the call to `security_file_alloc()` to the end of the `init_file()` function. Signed-off-by: Tianjia Zhang --- fs/file_table.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/fs/file_table.c b/fs/file_table.c index cd4a3db4659a..389eaf228499 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -156,11 +156,6 @@ static int init_file(struct file *f, int flags, const struct cred *cred) int error; f->f_cred = get_cred(cred); - error = security_file_alloc(f); - if (unlikely(error)) { - put_cred(f->f_cred); - return error; - } spin_lock_init(&f->f_lock); /* @@ -202,6 +197,14 @@ static int init_file(struct file *f, int flags, const struct cred *cred) * They may be enabled later by fsnotify_open_perm_and_set_mode(). */ file_set_fsnotify_mode(f, FMODE_NONOTIFY_PERM); + + error = security_file_alloc(f); + if (unlikely(error)) { + mutex_destroy(&f->f_pos_lock); + put_cred(f->f_cred); + return error; + } + return 0; } From c855d13470f42d2ff585d0f07735b209994a9db8 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Fri, 26 Jan 2024 18:23:13 +0800 Subject: [PATCH 005/110] x86/lib: Add setjmp/longjmp implementation Standard C provides basic library functions for jumping between functions. They were introduced to support the upcoming Lua language interpreter, which uses the setjmp/longjmp functions to implement exception handling. Signed-off-by: Tianjia Zhang --- arch/x86/Kconfig | 1 + arch/x86/include/asm/setjmp.h | 20 +++++++++++ arch/x86/lib/Makefile | 2 ++ arch/x86/lib/setjmp_32.S | 39 ++++++++++++++++++++++ arch/x86/lib/setjmp_64.S | 50 ++++++++++++++++++++++++++++ include/asm-generic/asm-prototypes.h | 5 +++ include/linux/setjmp.h | 7 ++++ 7 files changed, 124 insertions(+) create mode 100644 arch/x86/include/asm/setjmp.h create mode 100644 arch/x86/lib/setjmp_32.S create mode 100644 arch/x86/lib/setjmp_64.S create mode 100644 include/linux/setjmp.h diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 80527299f859..3eeb4b7e833a 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -105,6 +105,7 @@ config X86 select ARCH_HAS_COPY_MC if X86_64 select ARCH_HAS_SET_MEMORY select ARCH_HAS_SET_DIRECT_MAP + select ARCH_HAS_SETJMP select ARCH_HAS_STRICT_KERNEL_RWX select ARCH_HAS_STRICT_MODULE_RWX select ARCH_HAS_SYNC_CORE_BEFORE_USERMODE diff --git a/arch/x86/include/asm/setjmp.h b/arch/x86/include/asm/setjmp.h new file mode 100644 index 000000000000..3667166ed078 --- /dev/null +++ b/arch/x86/include/asm/setjmp.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_X86_SETJMP_H +#define _ASM_X86_SETJMP_H + +#include + +struct label_t { +#ifdef CONFIG_X86_32 + /* ABI (ebx, esp, ebp, esi, edi) and eip. */ + uint32_t regs[6]; +#else + /* ABI (rbx, rsp, rbp, r12-r15) and rip. */ + uint64_t regs[8]; +#endif +}; + +extern int setjmp(struct label_t *label); +extern void longjmp(struct label_t *label, int val); + +#endif /* _ASM_X86_SETJMP_H */ diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile index 2dba7f83ef97..dbb0ce8374bc 100644 --- a/arch/x86/lib/Makefile +++ b/arch/x86/lib/Makefile @@ -41,6 +41,8 @@ lib-$(CONFIG_MITIGATION_RETPOLINE) += retpoline.o obj-y += msr.o msr-reg.o msr-reg-export.o hweight.o obj-y += iomem.o +obj-$(CONFIG_SETJMP_ARCH) += setjmp_$(BITS).o + ifeq ($(CONFIG_X86_32),y) obj-y += atomic64_32.o lib-y += atomic64_cx8_32.o diff --git a/arch/x86/lib/setjmp_32.S b/arch/x86/lib/setjmp_32.S new file mode 100644 index 000000000000..dbc38975466a --- /dev/null +++ b/arch/x86/lib/setjmp_32.S @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#include +#include + +/* + * int setjmp(struct label_t *label); + */ +SYM_FUNC_START(setjmp) + movl 4(%esp), %eax + movl %ebx, (%eax) /* save ebx */ + movl %esp, 4(%eax) /* save esp */ + movl %ebp, 8(%eax) /* save ebp */ + movl %esi, 12(%eax) /* save esi */ + movl %edi, 16(%eax) /* save edi */ + movl (%esp), %edx /* get retaddr */ + movl %edx, 20(%eax) /* save eip */ + xorl %eax, %eax /* return 0 */ + RET +SYM_FUNC_END(setjmp) +EXPORT_SYMBOL(setjmp) + +/* + * void longjmp(struct label_t *label, int val); + */ +SYM_FUNC_START(longjmp) + movl 4(%esp), %eax + movl 8(%esp), %edx + movl (%eax), %ebx /* restore ebx */ + movl 4(%eax), %esp /* restore esp */ + movl 8(%eax), %ebp /* restore ebp */ + movl 12(%eax), %esi /* restore esi */ + movl 16(%eax), %edi /* restore edi */ + movl 20(%eax), %eax /* get retaddr */ + movl %eax, (%esp) /* put in return frame */ + movl %edx, %eax /* return val */ + RET +SYM_FUNC_END(longjmp) +EXPORT_SYMBOL(longjmp) diff --git a/arch/x86/lib/setjmp_64.S b/arch/x86/lib/setjmp_64.S new file mode 100644 index 000000000000..c6f409a0fd6f --- /dev/null +++ b/arch/x86/lib/setjmp_64.S @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#include +#include + +.section .noinstr.text, "ax" + +/* + * int setjmp(struct label_t *label); + */ +SYM_FUNC_START(setjmp) + /* + * Only save registers that must be preserved across function + * calls according to the ABI (%rbx, %rsp, %rbp, %r12-%r15) + * and %rip. + */ + movq %rdi, %rax + movq %rbx, (%rax) + movq %rsp, 8(%rax) + movq %rbp, 16(%rax) + movq %r12, 24(%rax) + movq %r13, 32(%rax) + movq %r14, 40(%rax) + movq %r15, 48(%rax) + movq (%rsp), %rdx + movq %rdx, 56(%rax) + xorq %rax, %rax + RET +SYM_FUNC_END(setjmp) +EXPORT_SYMBOL(setjmp) + +/* + * void longjmp(struct label_t *label, int val); + */ +SYM_FUNC_START(longjmp) + movq %rdi, %rax + movq (%rax), %rbx + movq 8(%rax), %rsp + movq 16(%rax), %rbp + movq 24(%rax), %r12 + movq 32(%rax), %r13 + movq 40(%rax), %r14 + movq 48(%rax), %r15 + movq 56(%rax), %rdx + movq %rdx, (%rsp) + movq %rsi, %rax + RET +SYM_FUNC_END(longjmp) +EXPORT_SYMBOL(longjmp) + diff --git a/include/asm-generic/asm-prototypes.h b/include/asm-generic/asm-prototypes.h index 2fa2bc208383..b6eb73343d14 100644 --- a/include/asm-generic/asm-prototypes.h +++ b/include/asm-generic/asm-prototypes.h @@ -12,3 +12,8 @@ extern void *memset(void *, int, __kernel_size_t); extern void *memcpy(void *, const void *, __kernel_size_t); #undef memmove extern void *memmove(void *, const void *, __kernel_size_t); +struct label_t; +#undef setjmp +extern int setjmp(struct label_t *); +#undef longjmp +extern void longjmp(struct label_t *, int); diff --git a/include/linux/setjmp.h b/include/linux/setjmp.h new file mode 100644 index 000000000000..ba58a6731ead --- /dev/null +++ b/include/linux/setjmp.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_SETJMP_H_ +#define _LINUX_SETJMP_H_ + +#include + +#endif /* _LINUX_SETJMP_H_ */ From d91f876f12303bcc5ec124949a8b059157ccfeed Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Sun, 9 Mar 2025 10:52:05 +0800 Subject: [PATCH 006/110] arm64/lib: Add setjmp/longjmp implementation Standard C provides basic library functions for jumping between functions. They were introduced to support the upcoming Lua language interpreter, which uses the setjmp/longjmp functions to implement exception handling. Signed-off-by: Tianjia Zhang --- arch/arm64/Kconfig | 1 + arch/arm64/include/asm/setjmp.h | 15 +++++++++++ arch/arm64/lib/Makefile | 2 ++ arch/arm64/lib/setjmp.S | 44 +++++++++++++++++++++++++++++++++ 4 files changed, 62 insertions(+) create mode 100644 arch/arm64/include/asm/setjmp.h create mode 100644 arch/arm64/lib/setjmp.S diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 93173f0a09c7..882c26b66e66 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -48,6 +48,7 @@ config ARM64 select ARCH_HAS_SETUP_DMA_OPS select ARCH_HAS_SET_DIRECT_MAP select ARCH_HAS_SET_MEMORY + select ARCH_HAS_SETJMP select ARCH_HAS_FORCE_DMA_UNENCRYPTED select ARCH_STACKWALK select ARCH_HAS_STRICT_KERNEL_RWX diff --git a/arch/arm64/include/asm/setjmp.h b/arch/arm64/include/asm/setjmp.h new file mode 100644 index 000000000000..a692e0f95beb --- /dev/null +++ b/arch/arm64/include/asm/setjmp.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_ARM64_SETJMP_H +#define _ASM_ARM64_SETJMP_H + +#include + +struct label_t { + /* ABI x19 .. x30 (lr), sp */ + uint64_t regs[13]; +}; + +extern int setjmp(struct label_t *label); +extern void longjmp(struct label_t *label, int val); + +#endif /* _ASM_ARM64_SETJMP_H */ diff --git a/arch/arm64/lib/Makefile b/arch/arm64/lib/Makefile index 633e5223d944..e21e72b2dd77 100644 --- a/arch/arm64/lib/Makefile +++ b/arch/arm64/lib/Makefile @@ -18,3 +18,5 @@ obj-$(CONFIG_FUNCTION_ERROR_INJECTION) += error-inject.o obj-$(CONFIG_ARM64_MTE) += mte.o obj-$(CONFIG_KASAN_SW_TAGS) += kasan_sw_tags.o + +obj-$(CONFIG_SETJMP_ARCH) += setjmp.o diff --git a/arch/arm64/lib/setjmp.S b/arch/arm64/lib/setjmp.S new file mode 100644 index 000000000000..bd7e00fe4387 --- /dev/null +++ b/arch/arm64/lib/setjmp.S @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2025 Alibaba Group. + */ + +#include +#include + + +/* + * int setjmp(struct label_t *label); + */ +.align 3 +SYM_FUNC_START(setjmp) + stp x19, x20, [x0, #(0 * 8)] + stp x21, x22, [x0, #(2 * 8)] + stp x23, x24, [x0, #(4 * 8)] + stp x25, x26, [x0, #(6 * 8)] + stp x27, x28, [x0, #(8 * 8)] + stp x29, x30, [x0, #(10 * 8)] + mov x1, sp + str x1, [x0, #(12 * 8)] + mov x0, #0 + ret +SYM_FUNC_END(setjmp) +EXPORT_SYMBOL(setjmp) + +/* + * void longjmp(struct label_t *label, int val); + */ +.align 3 +SYM_FUNC_START(longjmp) + ldp x19, x20, [x0, #(0 * 8)] + ldp x21, x22, [x0, #(2 * 8)] + ldp x23, x24, [x0, #(4 * 8)] + ldp x25, x26, [x0, #(6 * 8)] + ldp x27, x28, [x0, #(8 * 8)] + ldp x29, x30, [x0, #(10 * 8)] + ldr x0, [x0, #(12 * 8)] + mov sp, x0 + mov x0, x1 + ret +SYM_FUNC_END(longjmp) +EXPORT_SYMBOL(longjmp) From 77abccc60f64b5dccc4df2696f4c6f19442990b8 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Fri, 7 Mar 2025 14:48:57 +0800 Subject: [PATCH 007/110] lib: Introduced Lua 5.1 language interpreter Lua is an extension programming language designed to support general procedural programming with data description facilities. It also offers good support for object-oriented programming, functional programming, and data-driven programming. Lua is intended to be used as a powerful, light-weight scripting language for any program that needs one. Lua is implemented as a library, written in clean C (that is, in the common subset of ANSI C and C++). Lua is embedded in the kernel. Leveraging Lua's simplicity and powerful metaprogramming capabilities, it allows the use of scripting languages to develop complex security access control policies. The reason for choosing version 5.1 is that Lua has been mature enough since version 5.1, and secondly, LuaJIT may be introduced in the future for JIT acceleration. Currently, LuaJIT is compatible with version 5.1. Signed-off-by: Tianjia Zhang --- lib/lua/Makefile | 182 ++++++ lib/lua/lapi.c | 1087 +++++++++++++++++++++++++++++++++++ lib/lua/lapi.h | 16 + lib/lua/lauxlib.c | 652 +++++++++++++++++++++ lib/lua/lauxlib.h | 174 ++++++ lib/lua/lbaselib.c | 653 +++++++++++++++++++++ lib/lua/lcode.c | 831 +++++++++++++++++++++++++++ lib/lua/lcode.h | 76 +++ lib/lua/ldblib.c | 398 +++++++++++++ lib/lua/ldebug.c | 638 +++++++++++++++++++++ lib/lua/ldebug.h | 33 ++ lib/lua/ldo.c | 519 +++++++++++++++++ lib/lua/ldo.h | 57 ++ lib/lua/ldump.c | 164 ++++++ lib/lua/lfunc.c | 174 ++++++ lib/lua/lfunc.h | 34 ++ lib/lua/lgc.c | 710 +++++++++++++++++++++++ lib/lua/lgc.h | 110 ++++ lib/lua/linit.c | 38 ++ lib/lua/liolib.c | 556 ++++++++++++++++++ lib/lua/llex.c | 463 +++++++++++++++ lib/lua/llex.h | 81 +++ lib/lua/llimits.h | 128 +++++ lib/lua/lmathlib.c | 263 +++++++++ lib/lua/lmem.c | 86 +++ lib/lua/lmem.h | 49 ++ lib/lua/loadlib.c | 666 ++++++++++++++++++++++ lib/lua/lobject.c | 214 +++++++ lib/lua/lobject.h | 381 +++++++++++++ lib/lua/lopcodes.c | 102 ++++ lib/lua/lopcodes.h | 268 +++++++++ lib/lua/loslib.c | 243 ++++++++ lib/lua/lparser.c | 1339 ++++++++++++++++++++++++++++++++++++++++++++ lib/lua/lparser.h | 82 +++ lib/lua/lstate.c | 214 +++++++ lib/lua/lstate.h | 169 ++++++ lib/lua/lstring.c | 111 ++++ lib/lua/lstring.h | 31 + lib/lua/lstrlib.c | 871 ++++++++++++++++++++++++++++ lib/lua/ltable.c | 588 +++++++++++++++++++ lib/lua/ltable.h | 40 ++ lib/lua/ltablib.c | 287 ++++++++++ lib/lua/ltm.c | 75 +++ lib/lua/ltm.h | 54 ++ lib/lua/lua.c | 392 +++++++++++++ lib/lua/lua.h | 388 +++++++++++++ lib/lua/luac.c | 200 +++++++ lib/lua/luaconf.h | 763 +++++++++++++++++++++++++ lib/lua/lualib.h | 53 ++ lib/lua/lundump.c | 227 ++++++++ lib/lua/lundump.h | 36 ++ lib/lua/lvm.c | 767 +++++++++++++++++++++++++ lib/lua/lvm.h | 36 ++ lib/lua/lzio.c | 82 +++ lib/lua/lzio.h | 67 +++ lib/lua/print.c | 227 ++++++++ 56 files changed, 17145 insertions(+) create mode 100644 lib/lua/Makefile create mode 100644 lib/lua/lapi.c create mode 100644 lib/lua/lapi.h create mode 100644 lib/lua/lauxlib.c create mode 100644 lib/lua/lauxlib.h create mode 100644 lib/lua/lbaselib.c create mode 100644 lib/lua/lcode.c create mode 100644 lib/lua/lcode.h create mode 100644 lib/lua/ldblib.c create mode 100644 lib/lua/ldebug.c create mode 100644 lib/lua/ldebug.h create mode 100644 lib/lua/ldo.c create mode 100644 lib/lua/ldo.h create mode 100644 lib/lua/ldump.c create mode 100644 lib/lua/lfunc.c create mode 100644 lib/lua/lfunc.h create mode 100644 lib/lua/lgc.c create mode 100644 lib/lua/lgc.h create mode 100644 lib/lua/linit.c create mode 100644 lib/lua/liolib.c create mode 100644 lib/lua/llex.c create mode 100644 lib/lua/llex.h create mode 100644 lib/lua/llimits.h create mode 100644 lib/lua/lmathlib.c create mode 100644 lib/lua/lmem.c create mode 100644 lib/lua/lmem.h create mode 100644 lib/lua/loadlib.c create mode 100644 lib/lua/lobject.c create mode 100644 lib/lua/lobject.h create mode 100644 lib/lua/lopcodes.c create mode 100644 lib/lua/lopcodes.h create mode 100644 lib/lua/loslib.c create mode 100644 lib/lua/lparser.c create mode 100644 lib/lua/lparser.h create mode 100644 lib/lua/lstate.c create mode 100644 lib/lua/lstate.h create mode 100644 lib/lua/lstring.c create mode 100644 lib/lua/lstring.h create mode 100644 lib/lua/lstrlib.c create mode 100644 lib/lua/ltable.c create mode 100644 lib/lua/ltable.h create mode 100644 lib/lua/ltablib.c create mode 100644 lib/lua/ltm.c create mode 100644 lib/lua/ltm.h create mode 100644 lib/lua/lua.c create mode 100644 lib/lua/lua.h create mode 100644 lib/lua/luac.c create mode 100644 lib/lua/luaconf.h create mode 100644 lib/lua/lualib.h create mode 100644 lib/lua/lundump.c create mode 100644 lib/lua/lundump.h create mode 100644 lib/lua/lvm.c create mode 100644 lib/lua/lvm.h create mode 100644 lib/lua/lzio.c create mode 100644 lib/lua/lzio.h create mode 100644 lib/lua/print.c diff --git a/lib/lua/Makefile b/lib/lua/Makefile new file mode 100644 index 000000000000..e0d4c9fa649c --- /dev/null +++ b/lib/lua/Makefile @@ -0,0 +1,182 @@ +# makefile for building Lua +# see ../INSTALL for installation instructions +# see ../Makefile and luaconf.h for further customization + +# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT ======================= + +# Your platform. See PLATS for possible values. +PLAT= none + +CC= gcc +CFLAGS= -O2 -Wall $(MYCFLAGS) +AR= ar rcu +RANLIB= ranlib +RM= rm -f +LIBS= -lm $(MYLIBS) + +MYCFLAGS= +MYLDFLAGS= +MYLIBS= + +# == END OF USER SETTINGS. NO NEED TO CHANGE ANYTHING BELOW THIS LINE ========= + +PLATS= aix ansi bsd freebsd generic linux macosx mingw posix solaris + +LUA_A= liblua.a +CORE_O= lapi.o lcode.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o \ + lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o \ + lundump.o lvm.o lzio.o +LIB_O= lauxlib.o lbaselib.o ldblib.o liolib.o lmathlib.o loslib.o ltablib.o \ + lstrlib.o loadlib.o linit.o + +LUA_T= lua +LUA_O= lua.o + +LUAC_T= luac +LUAC_O= luac.o print.o + +ALL_O= $(CORE_O) $(LIB_O) $(LUA_O) $(LUAC_O) +ALL_T= $(LUA_A) $(LUA_T) $(LUAC_T) +ALL_A= $(LUA_A) + +default: $(PLAT) + +all: $(ALL_T) + +o: $(ALL_O) + +a: $(ALL_A) + +$(LUA_A): $(CORE_O) $(LIB_O) + $(AR) $@ $(CORE_O) $(LIB_O) # DLL needs all object files + $(RANLIB) $@ + +$(LUA_T): $(LUA_O) $(LUA_A) + $(CC) -o $@ $(MYLDFLAGS) $(LUA_O) $(LUA_A) $(LIBS) + +$(LUAC_T): $(LUAC_O) $(LUA_A) + $(CC) -o $@ $(MYLDFLAGS) $(LUAC_O) $(LUA_A) $(LIBS) + +clean: + $(RM) $(ALL_T) $(ALL_O) + +depend: + @$(CC) $(CFLAGS) -MM l*.c print.c + +echo: + @echo "PLAT = $(PLAT)" + @echo "CC = $(CC)" + @echo "CFLAGS = $(CFLAGS)" + @echo "AR = $(AR)" + @echo "RANLIB = $(RANLIB)" + @echo "RM = $(RM)" + @echo "MYCFLAGS = $(MYCFLAGS)" + @echo "MYLDFLAGS = $(MYLDFLAGS)" + @echo "MYLIBS = $(MYLIBS)" + +# convenience targets for popular platforms + +none: + @echo "Please choose a platform:" + @echo " $(PLATS)" + +aix: + $(MAKE) all CC="xlc" CFLAGS="-O2 -DLUA_USE_POSIX -DLUA_USE_DLOPEN" MYLIBS="-ldl" MYLDFLAGS="-brtl -bexpall" + +ansi: + $(MAKE) all MYCFLAGS=-DLUA_ANSI + +bsd: + $(MAKE) all MYCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN" MYLIBS="-Wl,-E" + +freebsd: + $(MAKE) all MYCFLAGS="-DLUA_USE_LINUX" MYLIBS="-Wl,-E -lreadline" + +generic: + $(MAKE) all MYCFLAGS= + +linux: + $(MAKE) all MYCFLAGS=-DLUA_USE_LINUX MYLIBS="-Wl,-E -ldl -lreadline -lhistory -lncurses" + +macosx: + $(MAKE) all MYCFLAGS=-DLUA_USE_LINUX MYLIBS="-lreadline" +# use this on Mac OS X 10.3- +# $(MAKE) all MYCFLAGS=-DLUA_USE_MACOSX + +mingw: + $(MAKE) "LUA_A=lua51.dll" "LUA_T=lua.exe" \ + "AR=$(CC) -shared -o" "RANLIB=strip --strip-unneeded" \ + "MYCFLAGS=-DLUA_BUILD_AS_DLL" "MYLIBS=" "MYLDFLAGS=-s" lua.exe + $(MAKE) "LUAC_T=luac.exe" luac.exe + +posix: + $(MAKE) all MYCFLAGS=-DLUA_USE_POSIX + +solaris: + $(MAKE) all MYCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN" MYLIBS="-ldl" + +# list targets that do not create files (but not all makes understand .PHONY) +.PHONY: all $(PLATS) default o a clean depend echo none + +# DO NOT DELETE + +lapi.o: lapi.c lua.h luaconf.h lapi.h lobject.h llimits.h ldebug.h \ + lstate.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h \ + lundump.h lvm.h +lauxlib.o: lauxlib.c lua.h luaconf.h lauxlib.h +lbaselib.o: lbaselib.c lua.h luaconf.h lauxlib.h lualib.h +lcode.o: lcode.c lua.h luaconf.h lcode.h llex.h lobject.h llimits.h \ + lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h ldo.h lgc.h \ + ltable.h +ldblib.o: ldblib.c lua.h luaconf.h lauxlib.h lualib.h +ldebug.o: ldebug.c lua.h luaconf.h lapi.h lobject.h llimits.h lcode.h \ + llex.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h ldo.h \ + lfunc.h lstring.h lgc.h ltable.h lvm.h +ldo.o: ldo.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \ + lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lparser.h lstring.h \ + ltable.h lundump.h lvm.h +ldump.o: ldump.c lua.h luaconf.h lobject.h llimits.h lstate.h ltm.h \ + lzio.h lmem.h lundump.h +lfunc.o: lfunc.c lua.h luaconf.h lfunc.h lobject.h llimits.h lgc.h lmem.h \ + lstate.h ltm.h lzio.h +lgc.o: lgc.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \ + lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h +linit.o: linit.c lua.h luaconf.h lualib.h lauxlib.h +liolib.o: liolib.c lua.h luaconf.h lauxlib.h lualib.h +llex.o: llex.c lua.h luaconf.h ldo.h lobject.h llimits.h lstate.h ltm.h \ + lzio.h lmem.h llex.h lparser.h lstring.h lgc.h ltable.h +lmathlib.o: lmathlib.c lua.h luaconf.h lauxlib.h lualib.h +lmem.o: lmem.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \ + ltm.h lzio.h lmem.h ldo.h +loadlib.o: loadlib.c lua.h luaconf.h lauxlib.h lualib.h +lobject.o: lobject.c lua.h luaconf.h ldo.h lobject.h llimits.h lstate.h \ + ltm.h lzio.h lmem.h lstring.h lgc.h lvm.h +lopcodes.o: lopcodes.c lopcodes.h llimits.h lua.h luaconf.h +loslib.o: loslib.c lua.h luaconf.h lauxlib.h lualib.h +lparser.o: lparser.c lua.h luaconf.h lcode.h llex.h lobject.h llimits.h \ + lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h ldo.h \ + lfunc.h lstring.h lgc.h ltable.h +lstate.o: lstate.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \ + ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h llex.h lstring.h ltable.h +lstring.o: lstring.c lua.h luaconf.h lmem.h llimits.h lobject.h lstate.h \ + ltm.h lzio.h lstring.h lgc.h +lstrlib.o: lstrlib.c lua.h luaconf.h lauxlib.h lualib.h +ltable.o: ltable.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \ + ltm.h lzio.h lmem.h ldo.h lgc.h ltable.h +ltablib.o: ltablib.c lua.h luaconf.h lauxlib.h lualib.h +ltm.o: ltm.c lua.h luaconf.h lobject.h llimits.h lstate.h ltm.h lzio.h \ + lmem.h lstring.h lgc.h ltable.h +lua.o: lua.c lua.h luaconf.h lauxlib.h lualib.h +luac.o: luac.c lua.h luaconf.h lauxlib.h ldo.h lobject.h llimits.h \ + lstate.h ltm.h lzio.h lmem.h lfunc.h lopcodes.h lstring.h lgc.h \ + lundump.h +lundump.o: lundump.c lua.h luaconf.h ldebug.h lstate.h lobject.h \ + llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lstring.h lgc.h lundump.h +lvm.o: lvm.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \ + lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lstring.h ltable.h lvm.h +lzio.o: lzio.c lua.h luaconf.h llimits.h lmem.h lstate.h lobject.h ltm.h \ + lzio.h +print.o: print.c ldebug.h lstate.h lua.h luaconf.h lobject.h llimits.h \ + ltm.h lzio.h lmem.h lopcodes.h lundump.h + +# (end of Makefile) diff --git a/lib/lua/lapi.c b/lib/lua/lapi.c new file mode 100644 index 000000000000..5d5145d2ebad --- /dev/null +++ b/lib/lua/lapi.c @@ -0,0 +1,1087 @@ +/* +** $Id: lapi.c,v 2.55.1.5 2008/07/04 18:41:18 roberto Exp $ +** Lua API +** See Copyright Notice in lua.h +*/ + + +#include +#include +#include +#include + +#define lapi_c +#define LUA_CORE + +#include "lua.h" + +#include "lapi.h" +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" +#include "lundump.h" +#include "lvm.h" + + + +const char lua_ident[] = + "$Lua: " LUA_RELEASE " " LUA_COPYRIGHT " $\n" + "$Authors: " LUA_AUTHORS " $\n" + "$URL: www.lua.org $\n"; + + + +#define api_checknelems(L, n) api_check(L, (n) <= (L->top - L->base)) + +#define api_checkvalidindex(L, i) api_check(L, (i) != luaO_nilobject) + +#define api_incr_top(L) {api_check(L, L->top < L->ci->top); L->top++;} + + + +static TValue *index2adr (lua_State *L, int idx) { + if (idx > 0) { + TValue *o = L->base + (idx - 1); + api_check(L, idx <= L->ci->top - L->base); + if (o >= L->top) return cast(TValue *, luaO_nilobject); + else return o; + } + else if (idx > LUA_REGISTRYINDEX) { + api_check(L, idx != 0 && -idx <= L->top - L->base); + return L->top + idx; + } + else switch (idx) { /* pseudo-indices */ + case LUA_REGISTRYINDEX: return registry(L); + case LUA_ENVIRONINDEX: { + Closure *func = curr_func(L); + sethvalue(L, &L->env, func->c.env); + return &L->env; + } + case LUA_GLOBALSINDEX: return gt(L); + default: { + Closure *func = curr_func(L); + idx = LUA_GLOBALSINDEX - idx; + return (idx <= func->c.nupvalues) + ? &func->c.upvalue[idx-1] + : cast(TValue *, luaO_nilobject); + } + } +} + + +static Table *getcurrenv (lua_State *L) { + if (L->ci == L->base_ci) /* no enclosing function? */ + return hvalue(gt(L)); /* use global table as environment */ + else { + Closure *func = curr_func(L); + return func->c.env; + } +} + + +void luaA_pushobject (lua_State *L, const TValue *o) { + setobj2s(L, L->top, o); + api_incr_top(L); +} + + +LUA_API int lua_checkstack (lua_State *L, int size) { + int res = 1; + lua_lock(L); + if (size > LUAI_MAXCSTACK || (L->top - L->base + size) > LUAI_MAXCSTACK) + res = 0; /* stack overflow */ + else if (size > 0) { + luaD_checkstack(L, size); + if (L->ci->top < L->top + size) + L->ci->top = L->top + size; + } + lua_unlock(L); + return res; +} + + +LUA_API void lua_xmove (lua_State *from, lua_State *to, int n) { + int i; + if (from == to) return; + lua_lock(to); + api_checknelems(from, n); + api_check(from, G(from) == G(to)); + api_check(from, to->ci->top - to->top >= n); + from->top -= n; + for (i = 0; i < n; i++) { + setobj2s(to, to->top++, from->top + i); + } + lua_unlock(to); +} + + +LUA_API void lua_setlevel (lua_State *from, lua_State *to) { + to->nCcalls = from->nCcalls; +} + + +LUA_API lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf) { + lua_CFunction old; + lua_lock(L); + old = G(L)->panic; + G(L)->panic = panicf; + lua_unlock(L); + return old; +} + + +LUA_API lua_State *lua_newthread (lua_State *L) { + lua_State *L1; + lua_lock(L); + luaC_checkGC(L); + L1 = luaE_newthread(L); + setthvalue(L, L->top, L1); + api_incr_top(L); + lua_unlock(L); + luai_userstatethread(L, L1); + return L1; +} + + + +/* +** basic stack manipulation +*/ + + +LUA_API int lua_gettop (lua_State *L) { + return cast_int(L->top - L->base); +} + + +LUA_API void lua_settop (lua_State *L, int idx) { + lua_lock(L); + if (idx >= 0) { + api_check(L, idx <= L->stack_last - L->base); + while (L->top < L->base + idx) + setnilvalue(L->top++); + L->top = L->base + idx; + } + else { + api_check(L, -(idx+1) <= (L->top - L->base)); + L->top += idx+1; /* `subtract' index (index is negative) */ + } + lua_unlock(L); +} + + +LUA_API void lua_remove (lua_State *L, int idx) { + StkId p; + lua_lock(L); + p = index2adr(L, idx); + api_checkvalidindex(L, p); + while (++p < L->top) setobjs2s(L, p-1, p); + L->top--; + lua_unlock(L); +} + + +LUA_API void lua_insert (lua_State *L, int idx) { + StkId p; + StkId q; + lua_lock(L); + p = index2adr(L, idx); + api_checkvalidindex(L, p); + for (q = L->top; q>p; q--) setobjs2s(L, q, q-1); + setobjs2s(L, p, L->top); + lua_unlock(L); +} + + +LUA_API void lua_replace (lua_State *L, int idx) { + StkId o; + lua_lock(L); + /* explicit test for incompatible code */ + if (idx == LUA_ENVIRONINDEX && L->ci == L->base_ci) + luaG_runerror(L, "no calling environment"); + api_checknelems(L, 1); + o = index2adr(L, idx); + api_checkvalidindex(L, o); + if (idx == LUA_ENVIRONINDEX) { + Closure *func = curr_func(L); + api_check(L, ttistable(L->top - 1)); + func->c.env = hvalue(L->top - 1); + luaC_barrier(L, func, L->top - 1); + } + else { + setobj(L, o, L->top - 1); + if (idx < LUA_GLOBALSINDEX) /* function upvalue? */ + luaC_barrier(L, curr_func(L), L->top - 1); + } + L->top--; + lua_unlock(L); +} + + +LUA_API void lua_pushvalue (lua_State *L, int idx) { + lua_lock(L); + setobj2s(L, L->top, index2adr(L, idx)); + api_incr_top(L); + lua_unlock(L); +} + + + +/* +** access functions (stack -> C) +*/ + + +LUA_API int lua_type (lua_State *L, int idx) { + StkId o = index2adr(L, idx); + return (o == luaO_nilobject) ? LUA_TNONE : ttype(o); +} + + +LUA_API const char *lua_typename (lua_State *L, int t) { + UNUSED(L); + return (t == LUA_TNONE) ? "no value" : luaT_typenames[t]; +} + + +LUA_API int lua_iscfunction (lua_State *L, int idx) { + StkId o = index2adr(L, idx); + return iscfunction(o); +} + + +LUA_API int lua_isnumber (lua_State *L, int idx) { + TValue n; + const TValue *o = index2adr(L, idx); + return tonumber(o, &n); +} + + +LUA_API int lua_isstring (lua_State *L, int idx) { + int t = lua_type(L, idx); + return (t == LUA_TSTRING || t == LUA_TNUMBER); +} + + +LUA_API int lua_isuserdata (lua_State *L, int idx) { + const TValue *o = index2adr(L, idx); + return (ttisuserdata(o) || ttislightuserdata(o)); +} + + +LUA_API int lua_rawequal (lua_State *L, int index1, int index2) { + StkId o1 = index2adr(L, index1); + StkId o2 = index2adr(L, index2); + return (o1 == luaO_nilobject || o2 == luaO_nilobject) ? 0 + : luaO_rawequalObj(o1, o2); +} + + +LUA_API int lua_equal (lua_State *L, int index1, int index2) { + StkId o1, o2; + int i; + lua_lock(L); /* may call tag method */ + o1 = index2adr(L, index1); + o2 = index2adr(L, index2); + i = (o1 == luaO_nilobject || o2 == luaO_nilobject) ? 0 : equalobj(L, o1, o2); + lua_unlock(L); + return i; +} + + +LUA_API int lua_lessthan (lua_State *L, int index1, int index2) { + StkId o1, o2; + int i; + lua_lock(L); /* may call tag method */ + o1 = index2adr(L, index1); + o2 = index2adr(L, index2); + i = (o1 == luaO_nilobject || o2 == luaO_nilobject) ? 0 + : luaV_lessthan(L, o1, o2); + lua_unlock(L); + return i; +} + + + +LUA_API lua_Number lua_tonumber (lua_State *L, int idx) { + TValue n; + const TValue *o = index2adr(L, idx); + if (tonumber(o, &n)) + return nvalue(o); + else + return 0; +} + + +LUA_API lua_Integer lua_tointeger (lua_State *L, int idx) { + TValue n; + const TValue *o = index2adr(L, idx); + if (tonumber(o, &n)) { + lua_Integer res; + lua_Number num = nvalue(o); + lua_number2integer(res, num); + return res; + } + else + return 0; +} + + +LUA_API int lua_toboolean (lua_State *L, int idx) { + const TValue *o = index2adr(L, idx); + return !l_isfalse(o); +} + + +LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) { + StkId o = index2adr(L, idx); + if (!ttisstring(o)) { + lua_lock(L); /* `luaV_tostring' may create a new string */ + if (!luaV_tostring(L, o)) { /* conversion failed? */ + if (len != NULL) *len = 0; + lua_unlock(L); + return NULL; + } + luaC_checkGC(L); + o = index2adr(L, idx); /* previous call may reallocate the stack */ + lua_unlock(L); + } + if (len != NULL) *len = tsvalue(o)->len; + return svalue(o); +} + + +LUA_API size_t lua_objlen (lua_State *L, int idx) { + StkId o = index2adr(L, idx); + switch (ttype(o)) { + case LUA_TSTRING: return tsvalue(o)->len; + case LUA_TUSERDATA: return uvalue(o)->len; + case LUA_TTABLE: return luaH_getn(hvalue(o)); + case LUA_TNUMBER: { + size_t l; + lua_lock(L); /* `luaV_tostring' may create a new string */ + l = (luaV_tostring(L, o) ? tsvalue(o)->len : 0); + lua_unlock(L); + return l; + } + default: return 0; + } +} + + +LUA_API lua_CFunction lua_tocfunction (lua_State *L, int idx) { + StkId o = index2adr(L, idx); + return (!iscfunction(o)) ? NULL : clvalue(o)->c.f; +} + + +LUA_API void *lua_touserdata (lua_State *L, int idx) { + StkId o = index2adr(L, idx); + switch (ttype(o)) { + case LUA_TUSERDATA: return (rawuvalue(o) + 1); + case LUA_TLIGHTUSERDATA: return pvalue(o); + default: return NULL; + } +} + + +LUA_API lua_State *lua_tothread (lua_State *L, int idx) { + StkId o = index2adr(L, idx); + return (!ttisthread(o)) ? NULL : thvalue(o); +} + + +LUA_API const void *lua_topointer (lua_State *L, int idx) { + StkId o = index2adr(L, idx); + switch (ttype(o)) { + case LUA_TTABLE: return hvalue(o); + case LUA_TFUNCTION: return clvalue(o); + case LUA_TTHREAD: return thvalue(o); + case LUA_TUSERDATA: + case LUA_TLIGHTUSERDATA: + return lua_touserdata(L, idx); + default: return NULL; + } +} + + + +/* +** push functions (C -> stack) +*/ + + +LUA_API void lua_pushnil (lua_State *L) { + lua_lock(L); + setnilvalue(L->top); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_pushnumber (lua_State *L, lua_Number n) { + lua_lock(L); + setnvalue(L->top, n); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_pushinteger (lua_State *L, lua_Integer n) { + lua_lock(L); + setnvalue(L->top, cast_num(n)); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_pushlstring (lua_State *L, const char *s, size_t len) { + lua_lock(L); + luaC_checkGC(L); + setsvalue2s(L, L->top, luaS_newlstr(L, s, len)); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_pushstring (lua_State *L, const char *s) { + if (s == NULL) + lua_pushnil(L); + else + lua_pushlstring(L, s, strlen(s)); +} + + +LUA_API const char *lua_pushvfstring (lua_State *L, const char *fmt, + va_list argp) { + const char *ret; + lua_lock(L); + luaC_checkGC(L); + ret = luaO_pushvfstring(L, fmt, argp); + lua_unlock(L); + return ret; +} + + +LUA_API const char *lua_pushfstring (lua_State *L, const char *fmt, ...) { + const char *ret; + va_list argp; + lua_lock(L); + luaC_checkGC(L); + va_start(argp, fmt); + ret = luaO_pushvfstring(L, fmt, argp); + va_end(argp); + lua_unlock(L); + return ret; +} + + +LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) { + Closure *cl; + lua_lock(L); + luaC_checkGC(L); + api_checknelems(L, n); + cl = luaF_newCclosure(L, n, getcurrenv(L)); + cl->c.f = fn; + L->top -= n; + while (n--) + setobj2n(L, &cl->c.upvalue[n], L->top+n); + setclvalue(L, L->top, cl); + lua_assert(iswhite(obj2gco(cl))); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_pushboolean (lua_State *L, int b) { + lua_lock(L); + setbvalue(L->top, (b != 0)); /* ensure that true is 1 */ + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_pushlightuserdata (lua_State *L, void *p) { + lua_lock(L); + setpvalue(L->top, p); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API int lua_pushthread (lua_State *L) { + lua_lock(L); + setthvalue(L, L->top, L); + api_incr_top(L); + lua_unlock(L); + return (G(L)->mainthread == L); +} + + + +/* +** get functions (Lua -> stack) +*/ + + +LUA_API void lua_gettable (lua_State *L, int idx) { + StkId t; + lua_lock(L); + t = index2adr(L, idx); + api_checkvalidindex(L, t); + luaV_gettable(L, t, L->top - 1, L->top - 1); + lua_unlock(L); +} + + +LUA_API void lua_getfield (lua_State *L, int idx, const char *k) { + StkId t; + TValue key; + lua_lock(L); + t = index2adr(L, idx); + api_checkvalidindex(L, t); + setsvalue(L, &key, luaS_new(L, k)); + luaV_gettable(L, t, &key, L->top); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_rawget (lua_State *L, int idx) { + StkId t; + lua_lock(L); + t = index2adr(L, idx); + api_check(L, ttistable(t)); + setobj2s(L, L->top - 1, luaH_get(hvalue(t), L->top - 1)); + lua_unlock(L); +} + + +LUA_API void lua_rawgeti (lua_State *L, int idx, int n) { + StkId o; + lua_lock(L); + o = index2adr(L, idx); + api_check(L, ttistable(o)); + setobj2s(L, L->top, luaH_getnum(hvalue(o), n)); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_createtable (lua_State *L, int narray, int nrec) { + lua_lock(L); + luaC_checkGC(L); + sethvalue(L, L->top, luaH_new(L, narray, nrec)); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API int lua_getmetatable (lua_State *L, int objindex) { + const TValue *obj; + Table *mt = NULL; + int res; + lua_lock(L); + obj = index2adr(L, objindex); + switch (ttype(obj)) { + case LUA_TTABLE: + mt = hvalue(obj)->metatable; + break; + case LUA_TUSERDATA: + mt = uvalue(obj)->metatable; + break; + default: + mt = G(L)->mt[ttype(obj)]; + break; + } + if (mt == NULL) + res = 0; + else { + sethvalue(L, L->top, mt); + api_incr_top(L); + res = 1; + } + lua_unlock(L); + return res; +} + + +LUA_API void lua_getfenv (lua_State *L, int idx) { + StkId o; + lua_lock(L); + o = index2adr(L, idx); + api_checkvalidindex(L, o); + switch (ttype(o)) { + case LUA_TFUNCTION: + sethvalue(L, L->top, clvalue(o)->c.env); + break; + case LUA_TUSERDATA: + sethvalue(L, L->top, uvalue(o)->env); + break; + case LUA_TTHREAD: + setobj2s(L, L->top, gt(thvalue(o))); + break; + default: + setnilvalue(L->top); + break; + } + api_incr_top(L); + lua_unlock(L); +} + + +/* +** set functions (stack -> Lua) +*/ + + +LUA_API void lua_settable (lua_State *L, int idx) { + StkId t; + lua_lock(L); + api_checknelems(L, 2); + t = index2adr(L, idx); + api_checkvalidindex(L, t); + luaV_settable(L, t, L->top - 2, L->top - 1); + L->top -= 2; /* pop index and value */ + lua_unlock(L); +} + + +LUA_API void lua_setfield (lua_State *L, int idx, const char *k) { + StkId t; + TValue key; + lua_lock(L); + api_checknelems(L, 1); + t = index2adr(L, idx); + api_checkvalidindex(L, t); + setsvalue(L, &key, luaS_new(L, k)); + luaV_settable(L, t, &key, L->top - 1); + L->top--; /* pop value */ + lua_unlock(L); +} + + +LUA_API void lua_rawset (lua_State *L, int idx) { + StkId t; + lua_lock(L); + api_checknelems(L, 2); + t = index2adr(L, idx); + api_check(L, ttistable(t)); + setobj2t(L, luaH_set(L, hvalue(t), L->top-2), L->top-1); + luaC_barriert(L, hvalue(t), L->top-1); + L->top -= 2; + lua_unlock(L); +} + + +LUA_API void lua_rawseti (lua_State *L, int idx, int n) { + StkId o; + lua_lock(L); + api_checknelems(L, 1); + o = index2adr(L, idx); + api_check(L, ttistable(o)); + setobj2t(L, luaH_setnum(L, hvalue(o), n), L->top-1); + luaC_barriert(L, hvalue(o), L->top-1); + L->top--; + lua_unlock(L); +} + + +LUA_API int lua_setmetatable (lua_State *L, int objindex) { + TValue *obj; + Table *mt; + lua_lock(L); + api_checknelems(L, 1); + obj = index2adr(L, objindex); + api_checkvalidindex(L, obj); + if (ttisnil(L->top - 1)) + mt = NULL; + else { + api_check(L, ttistable(L->top - 1)); + mt = hvalue(L->top - 1); + } + switch (ttype(obj)) { + case LUA_TTABLE: { + hvalue(obj)->metatable = mt; + if (mt) + luaC_objbarriert(L, hvalue(obj), mt); + break; + } + case LUA_TUSERDATA: { + uvalue(obj)->metatable = mt; + if (mt) + luaC_objbarrier(L, rawuvalue(obj), mt); + break; + } + default: { + G(L)->mt[ttype(obj)] = mt; + break; + } + } + L->top--; + lua_unlock(L); + return 1; +} + + +LUA_API int lua_setfenv (lua_State *L, int idx) { + StkId o; + int res = 1; + lua_lock(L); + api_checknelems(L, 1); + o = index2adr(L, idx); + api_checkvalidindex(L, o); + api_check(L, ttistable(L->top - 1)); + switch (ttype(o)) { + case LUA_TFUNCTION: + clvalue(o)->c.env = hvalue(L->top - 1); + break; + case LUA_TUSERDATA: + uvalue(o)->env = hvalue(L->top - 1); + break; + case LUA_TTHREAD: + sethvalue(L, gt(thvalue(o)), hvalue(L->top - 1)); + break; + default: + res = 0; + break; + } + if (res) luaC_objbarrier(L, gcvalue(o), hvalue(L->top - 1)); + L->top--; + lua_unlock(L); + return res; +} + + +/* +** `load' and `call' functions (run Lua code) +*/ + + +#define adjustresults(L,nres) \ + { if (nres == LUA_MULTRET && L->top >= L->ci->top) L->ci->top = L->top; } + + +#define checkresults(L,na,nr) \ + api_check(L, (nr) == LUA_MULTRET || (L->ci->top - L->top >= (nr) - (na))) + + +LUA_API void lua_call (lua_State *L, int nargs, int nresults) { + StkId func; + lua_lock(L); + api_checknelems(L, nargs+1); + checkresults(L, nargs, nresults); + func = L->top - (nargs+1); + luaD_call(L, func, nresults); + adjustresults(L, nresults); + lua_unlock(L); +} + + + +/* +** Execute a protected call. +*/ +struct CallS { /* data to `f_call' */ + StkId func; + int nresults; +}; + + +static void f_call (lua_State *L, void *ud) { + struct CallS *c = cast(struct CallS *, ud); + luaD_call(L, c->func, c->nresults); +} + + + +LUA_API int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc) { + struct CallS c; + int status; + ptrdiff_t func; + lua_lock(L); + api_checknelems(L, nargs+1); + checkresults(L, nargs, nresults); + if (errfunc == 0) + func = 0; + else { + StkId o = index2adr(L, errfunc); + api_checkvalidindex(L, o); + func = savestack(L, o); + } + c.func = L->top - (nargs+1); /* function to be called */ + c.nresults = nresults; + status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func); + adjustresults(L, nresults); + lua_unlock(L); + return status; +} + + +/* +** Execute a protected C call. +*/ +struct CCallS { /* data to `f_Ccall' */ + lua_CFunction func; + void *ud; +}; + + +static void f_Ccall (lua_State *L, void *ud) { + struct CCallS *c = cast(struct CCallS *, ud); + Closure *cl; + cl = luaF_newCclosure(L, 0, getcurrenv(L)); + cl->c.f = c->func; + setclvalue(L, L->top, cl); /* push function */ + api_incr_top(L); + setpvalue(L->top, c->ud); /* push only argument */ + api_incr_top(L); + luaD_call(L, L->top - 2, 0); +} + + +LUA_API int lua_cpcall (lua_State *L, lua_CFunction func, void *ud) { + struct CCallS c; + int status; + lua_lock(L); + c.func = func; + c.ud = ud; + status = luaD_pcall(L, f_Ccall, &c, savestack(L, L->top), 0); + lua_unlock(L); + return status; +} + + +LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data, + const char *chunkname) { + ZIO z; + int status; + lua_lock(L); + if (!chunkname) chunkname = "?"; + luaZ_init(L, &z, reader, data); + status = luaD_protectedparser(L, &z, chunkname); + lua_unlock(L); + return status; +} + + +LUA_API int lua_dump (lua_State *L, lua_Writer writer, void *data) { + int status; + TValue *o; + lua_lock(L); + api_checknelems(L, 1); + o = L->top - 1; + if (isLfunction(o)) + status = luaU_dump(L, clvalue(o)->l.p, writer, data, 0); + else + status = 1; + lua_unlock(L); + return status; +} + + +LUA_API int lua_status (lua_State *L) { + return L->status; +} + + +/* +** Garbage-collection function +*/ + +LUA_API int lua_gc (lua_State *L, int what, int data) { + int res = 0; + global_State *g; + lua_lock(L); + g = G(L); + switch (what) { + case LUA_GCSTOP: { + g->GCthreshold = MAX_LUMEM; + break; + } + case LUA_GCRESTART: { + g->GCthreshold = g->totalbytes; + break; + } + case LUA_GCCOLLECT: { + luaC_fullgc(L); + break; + } + case LUA_GCCOUNT: { + /* GC values are expressed in Kbytes: #bytes/2^10 */ + res = cast_int(g->totalbytes >> 10); + break; + } + case LUA_GCCOUNTB: { + res = cast_int(g->totalbytes & 0x3ff); + break; + } + case LUA_GCSTEP: { + lu_mem a = (cast(lu_mem, data) << 10); + if (a <= g->totalbytes) + g->GCthreshold = g->totalbytes - a; + else + g->GCthreshold = 0; + while (g->GCthreshold <= g->totalbytes) { + luaC_step(L); + if (g->gcstate == GCSpause) { /* end of cycle? */ + res = 1; /* signal it */ + break; + } + } + break; + } + case LUA_GCSETPAUSE: { + res = g->gcpause; + g->gcpause = data; + break; + } + case LUA_GCSETSTEPMUL: { + res = g->gcstepmul; + g->gcstepmul = data; + break; + } + default: res = -1; /* invalid option */ + } + lua_unlock(L); + return res; +} + + + +/* +** miscellaneous functions +*/ + + +LUA_API int lua_error (lua_State *L) { + lua_lock(L); + api_checknelems(L, 1); + luaG_errormsg(L); + lua_unlock(L); + return 0; /* to avoid warnings */ +} + + +LUA_API int lua_next (lua_State *L, int idx) { + StkId t; + int more; + lua_lock(L); + t = index2adr(L, idx); + api_check(L, ttistable(t)); + more = luaH_next(L, hvalue(t), L->top - 1); + if (more) { + api_incr_top(L); + } + else /* no more elements */ + L->top -= 1; /* remove key */ + lua_unlock(L); + return more; +} + + +LUA_API void lua_concat (lua_State *L, int n) { + lua_lock(L); + api_checknelems(L, n); + if (n >= 2) { + luaC_checkGC(L); + luaV_concat(L, n, cast_int(L->top - L->base) - 1); + L->top -= (n-1); + } + else if (n == 0) { /* push empty string */ + setsvalue2s(L, L->top, luaS_newlstr(L, "", 0)); + api_incr_top(L); + } + /* else n == 1; nothing to do */ + lua_unlock(L); +} + + +LUA_API lua_Alloc lua_getallocf (lua_State *L, void **ud) { + lua_Alloc f; + lua_lock(L); + if (ud) *ud = G(L)->ud; + f = G(L)->frealloc; + lua_unlock(L); + return f; +} + + +LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud) { + lua_lock(L); + G(L)->ud = ud; + G(L)->frealloc = f; + lua_unlock(L); +} + + +LUA_API void *lua_newuserdata (lua_State *L, size_t size) { + Udata *u; + lua_lock(L); + luaC_checkGC(L); + u = luaS_newudata(L, size, getcurrenv(L)); + setuvalue(L, L->top, u); + api_incr_top(L); + lua_unlock(L); + return u + 1; +} + + + + +static const char *aux_upvalue (StkId fi, int n, TValue **val) { + Closure *f; + if (!ttisfunction(fi)) return NULL; + f = clvalue(fi); + if (f->c.isC) { + if (!(1 <= n && n <= f->c.nupvalues)) return NULL; + *val = &f->c.upvalue[n-1]; + return ""; + } + else { + Proto *p = f->l.p; + if (!(1 <= n && n <= p->sizeupvalues)) return NULL; + *val = f->l.upvals[n-1]->v; + return getstr(p->upvalues[n-1]); + } +} + + +LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n) { + const char *name; + TValue *val; + lua_lock(L); + name = aux_upvalue(index2adr(L, funcindex), n, &val); + if (name) { + setobj2s(L, L->top, val); + api_incr_top(L); + } + lua_unlock(L); + return name; +} + + +LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) { + const char *name; + TValue *val; + StkId fi; + lua_lock(L); + fi = index2adr(L, funcindex); + api_checknelems(L, 1); + name = aux_upvalue(fi, n, &val); + if (name) { + L->top--; + setobj(L, val, L->top); + luaC_barrier(L, clvalue(fi), L->top); + } + lua_unlock(L); + return name; +} + diff --git a/lib/lua/lapi.h b/lib/lua/lapi.h new file mode 100644 index 000000000000..2c3fab244ef9 --- /dev/null +++ b/lib/lua/lapi.h @@ -0,0 +1,16 @@ +/* +** $Id: lapi.h,v 2.2.1.1 2007/12/27 13:02:25 roberto Exp $ +** Auxiliary functions from Lua API +** See Copyright Notice in lua.h +*/ + +#ifndef lapi_h +#define lapi_h + + +#include "lobject.h" + + +LUAI_FUNC void luaA_pushobject (lua_State *L, const TValue *o); + +#endif diff --git a/lib/lua/lauxlib.c b/lib/lua/lauxlib.c new file mode 100644 index 000000000000..10f14e2c088e --- /dev/null +++ b/lib/lua/lauxlib.c @@ -0,0 +1,652 @@ +/* +** $Id: lauxlib.c,v 1.159.1.3 2008/01/21 13:20:51 roberto Exp $ +** Auxiliary functions for building Lua libraries +** See Copyright Notice in lua.h +*/ + + +#include +#include +#include +#include +#include +#include + + +/* This file uses only the official API of Lua. +** Any function declared here could be written as an application function. +*/ + +#define lauxlib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" + + +#define FREELIST_REF 0 /* free list of references */ + + +/* convert a stack index to positive */ +#define abs_index(L, i) ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : \ + lua_gettop(L) + (i) + 1) + + +/* +** {====================================================== +** Error-report functions +** ======================================================= +*/ + + +LUALIB_API int luaL_argerror (lua_State *L, int narg, const char *extramsg) { + lua_Debug ar; + if (!lua_getstack(L, 0, &ar)) /* no stack frame? */ + return luaL_error(L, "bad argument #%d (%s)", narg, extramsg); + lua_getinfo(L, "n", &ar); + if (strcmp(ar.namewhat, "method") == 0) { + narg--; /* do not count `self' */ + if (narg == 0) /* error is in the self argument itself? */ + return luaL_error(L, "calling " LUA_QS " on bad self (%s)", + ar.name, extramsg); + } + if (ar.name == NULL) + ar.name = "?"; + return luaL_error(L, "bad argument #%d to " LUA_QS " (%s)", + narg, ar.name, extramsg); +} + + +LUALIB_API int luaL_typerror (lua_State *L, int narg, const char *tname) { + const char *msg = lua_pushfstring(L, "%s expected, got %s", + tname, luaL_typename(L, narg)); + return luaL_argerror(L, narg, msg); +} + + +static void tag_error (lua_State *L, int narg, int tag) { + luaL_typerror(L, narg, lua_typename(L, tag)); +} + + +LUALIB_API void luaL_where (lua_State *L, int level) { + lua_Debug ar; + if (lua_getstack(L, level, &ar)) { /* check function at level */ + lua_getinfo(L, "Sl", &ar); /* get info about it */ + if (ar.currentline > 0) { /* is there info? */ + lua_pushfstring(L, "%s:%d: ", ar.short_src, ar.currentline); + return; + } + } + lua_pushliteral(L, ""); /* else, no information available... */ +} + + +LUALIB_API int luaL_error (lua_State *L, const char *fmt, ...) { + va_list argp; + va_start(argp, fmt); + luaL_where(L, 1); + lua_pushvfstring(L, fmt, argp); + va_end(argp); + lua_concat(L, 2); + return lua_error(L); +} + +/* }====================================================== */ + + +LUALIB_API int luaL_checkoption (lua_State *L, int narg, const char *def, + const char *const lst[]) { + const char *name = (def) ? luaL_optstring(L, narg, def) : + luaL_checkstring(L, narg); + int i; + for (i=0; lst[i]; i++) + if (strcmp(lst[i], name) == 0) + return i; + return luaL_argerror(L, narg, + lua_pushfstring(L, "invalid option " LUA_QS, name)); +} + + +LUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) { + lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get registry.name */ + if (!lua_isnil(L, -1)) /* name already in use? */ + return 0; /* leave previous value on top, but return 0 */ + lua_pop(L, 1); + lua_newtable(L); /* create metatable */ + lua_pushvalue(L, -1); + lua_setfield(L, LUA_REGISTRYINDEX, tname); /* registry.name = metatable */ + return 1; +} + + +LUALIB_API void *luaL_checkudata (lua_State *L, int ud, const char *tname) { + void *p = lua_touserdata(L, ud); + if (p != NULL) { /* value is a userdata? */ + if (lua_getmetatable(L, ud)) { /* does it have a metatable? */ + lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get correct metatable */ + if (lua_rawequal(L, -1, -2)) { /* does it have the correct mt? */ + lua_pop(L, 2); /* remove both metatables */ + return p; + } + } + } + luaL_typerror(L, ud, tname); /* else error */ + return NULL; /* to avoid warnings */ +} + + +LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *mes) { + if (!lua_checkstack(L, space)) + luaL_error(L, "stack overflow (%s)", mes); +} + + +LUALIB_API void luaL_checktype (lua_State *L, int narg, int t) { + if (lua_type(L, narg) != t) + tag_error(L, narg, t); +} + + +LUALIB_API void luaL_checkany (lua_State *L, int narg) { + if (lua_type(L, narg) == LUA_TNONE) + luaL_argerror(L, narg, "value expected"); +} + + +LUALIB_API const char *luaL_checklstring (lua_State *L, int narg, size_t *len) { + const char *s = lua_tolstring(L, narg, len); + if (!s) tag_error(L, narg, LUA_TSTRING); + return s; +} + + +LUALIB_API const char *luaL_optlstring (lua_State *L, int narg, + const char *def, size_t *len) { + if (lua_isnoneornil(L, narg)) { + if (len) + *len = (def ? strlen(def) : 0); + return def; + } + else return luaL_checklstring(L, narg, len); +} + + +LUALIB_API lua_Number luaL_checknumber (lua_State *L, int narg) { + lua_Number d = lua_tonumber(L, narg); + if (d == 0 && !lua_isnumber(L, narg)) /* avoid extra test when d is not 0 */ + tag_error(L, narg, LUA_TNUMBER); + return d; +} + + +LUALIB_API lua_Number luaL_optnumber (lua_State *L, int narg, lua_Number def) { + return luaL_opt(L, luaL_checknumber, narg, def); +} + + +LUALIB_API lua_Integer luaL_checkinteger (lua_State *L, int narg) { + lua_Integer d = lua_tointeger(L, narg); + if (d == 0 && !lua_isnumber(L, narg)) /* avoid extra test when d is not 0 */ + tag_error(L, narg, LUA_TNUMBER); + return d; +} + + +LUALIB_API lua_Integer luaL_optinteger (lua_State *L, int narg, + lua_Integer def) { + return luaL_opt(L, luaL_checkinteger, narg, def); +} + + +LUALIB_API int luaL_getmetafield (lua_State *L, int obj, const char *event) { + if (!lua_getmetatable(L, obj)) /* no metatable? */ + return 0; + lua_pushstring(L, event); + lua_rawget(L, -2); + if (lua_isnil(L, -1)) { + lua_pop(L, 2); /* remove metatable and metafield */ + return 0; + } + else { + lua_remove(L, -2); /* remove only metatable */ + return 1; + } +} + + +LUALIB_API int luaL_callmeta (lua_State *L, int obj, const char *event) { + obj = abs_index(L, obj); + if (!luaL_getmetafield(L, obj, event)) /* no metafield? */ + return 0; + lua_pushvalue(L, obj); + lua_call(L, 1, 1); + return 1; +} + + +LUALIB_API void (luaL_register) (lua_State *L, const char *libname, + const luaL_Reg *l) { + luaI_openlib(L, libname, l, 0); +} + + +static int libsize (const luaL_Reg *l) { + int size = 0; + for (; l->name; l++) size++; + return size; +} + + +LUALIB_API void luaI_openlib (lua_State *L, const char *libname, + const luaL_Reg *l, int nup) { + if (libname) { + int size = libsize(l); + /* check whether lib already exists */ + luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 1); + lua_getfield(L, -1, libname); /* get _LOADED[libname] */ + if (!lua_istable(L, -1)) { /* not found? */ + lua_pop(L, 1); /* remove previous result */ + /* try global variable (and create one if it does not exist) */ + if (luaL_findtable(L, LUA_GLOBALSINDEX, libname, size) != NULL) + luaL_error(L, "name conflict for module " LUA_QS, libname); + lua_pushvalue(L, -1); + lua_setfield(L, -3, libname); /* _LOADED[libname] = new table */ + } + lua_remove(L, -2); /* remove _LOADED table */ + lua_insert(L, -(nup+1)); /* move library table to below upvalues */ + } + for (; l->name; l++) { + int i; + for (i=0; ifunc, nup); + lua_setfield(L, -(nup+2), l->name); + } + lua_pop(L, nup); /* remove upvalues */ +} + + + +/* +** {====================================================== +** getn-setn: size for arrays +** ======================================================= +*/ + +#if defined(LUA_COMPAT_GETN) + +static int checkint (lua_State *L, int topop) { + int n = (lua_type(L, -1) == LUA_TNUMBER) ? lua_tointeger(L, -1) : -1; + lua_pop(L, topop); + return n; +} + + +static void getsizes (lua_State *L) { + lua_getfield(L, LUA_REGISTRYINDEX, "LUA_SIZES"); + if (lua_isnil(L, -1)) { /* no `size' table? */ + lua_pop(L, 1); /* remove nil */ + lua_newtable(L); /* create it */ + lua_pushvalue(L, -1); /* `size' will be its own metatable */ + lua_setmetatable(L, -2); + lua_pushliteral(L, "kv"); + lua_setfield(L, -2, "__mode"); /* metatable(N).__mode = "kv" */ + lua_pushvalue(L, -1); + lua_setfield(L, LUA_REGISTRYINDEX, "LUA_SIZES"); /* store in register */ + } +} + + +LUALIB_API void luaL_setn (lua_State *L, int t, int n) { + t = abs_index(L, t); + lua_pushliteral(L, "n"); + lua_rawget(L, t); + if (checkint(L, 1) >= 0) { /* is there a numeric field `n'? */ + lua_pushliteral(L, "n"); /* use it */ + lua_pushinteger(L, n); + lua_rawset(L, t); + } + else { /* use `sizes' */ + getsizes(L); + lua_pushvalue(L, t); + lua_pushinteger(L, n); + lua_rawset(L, -3); /* sizes[t] = n */ + lua_pop(L, 1); /* remove `sizes' */ + } +} + + +LUALIB_API int luaL_getn (lua_State *L, int t) { + int n; + t = abs_index(L, t); + lua_pushliteral(L, "n"); /* try t.n */ + lua_rawget(L, t); + if ((n = checkint(L, 1)) >= 0) return n; + getsizes(L); /* else try sizes[t] */ + lua_pushvalue(L, t); + lua_rawget(L, -2); + if ((n = checkint(L, 2)) >= 0) return n; + return (int)lua_objlen(L, t); +} + +#endif + +/* }====================================================== */ + + + +LUALIB_API const char *luaL_gsub (lua_State *L, const char *s, const char *p, + const char *r) { + const char *wild; + size_t l = strlen(p); + luaL_Buffer b; + luaL_buffinit(L, &b); + while ((wild = strstr(s, p)) != NULL) { + luaL_addlstring(&b, s, wild - s); /* push prefix */ + luaL_addstring(&b, r); /* push replacement in place of pattern */ + s = wild + l; /* continue after `p' */ + } + luaL_addstring(&b, s); /* push last suffix */ + luaL_pushresult(&b); + return lua_tostring(L, -1); +} + + +LUALIB_API const char *luaL_findtable (lua_State *L, int idx, + const char *fname, int szhint) { + const char *e; + lua_pushvalue(L, idx); + do { + e = strchr(fname, '.'); + if (e == NULL) e = fname + strlen(fname); + lua_pushlstring(L, fname, e - fname); + lua_rawget(L, -2); + if (lua_isnil(L, -1)) { /* no such field? */ + lua_pop(L, 1); /* remove this nil */ + lua_createtable(L, 0, (*e == '.' ? 1 : szhint)); /* new table for field */ + lua_pushlstring(L, fname, e - fname); + lua_pushvalue(L, -2); + lua_settable(L, -4); /* set new table into field */ + } + else if (!lua_istable(L, -1)) { /* field has a non-table value? */ + lua_pop(L, 2); /* remove table and value */ + return fname; /* return problematic part of the name */ + } + lua_remove(L, -2); /* remove previous table */ + fname = e + 1; + } while (*e == '.'); + return NULL; +} + + + +/* +** {====================================================== +** Generic Buffer manipulation +** ======================================================= +*/ + + +#define bufflen(B) ((B)->p - (B)->buffer) +#define bufffree(B) ((size_t)(LUAL_BUFFERSIZE - bufflen(B))) + +#define LIMIT (LUA_MINSTACK/2) + + +static int emptybuffer (luaL_Buffer *B) { + size_t l = bufflen(B); + if (l == 0) return 0; /* put nothing on stack */ + else { + lua_pushlstring(B->L, B->buffer, l); + B->p = B->buffer; + B->lvl++; + return 1; + } +} + + +static void adjuststack (luaL_Buffer *B) { + if (B->lvl > 1) { + lua_State *L = B->L; + int toget = 1; /* number of levels to concat */ + size_t toplen = lua_strlen(L, -1); + do { + size_t l = lua_strlen(L, -(toget+1)); + if (B->lvl - toget + 1 >= LIMIT || toplen > l) { + toplen += l; + toget++; + } + else break; + } while (toget < B->lvl); + lua_concat(L, toget); + B->lvl = B->lvl - toget + 1; + } +} + + +LUALIB_API char *luaL_prepbuffer (luaL_Buffer *B) { + if (emptybuffer(B)) + adjuststack(B); + return B->buffer; +} + + +LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) { + while (l--) + luaL_addchar(B, *s++); +} + + +LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) { + luaL_addlstring(B, s, strlen(s)); +} + + +LUALIB_API void luaL_pushresult (luaL_Buffer *B) { + emptybuffer(B); + lua_concat(B->L, B->lvl); + B->lvl = 1; +} + + +LUALIB_API void luaL_addvalue (luaL_Buffer *B) { + lua_State *L = B->L; + size_t vl; + const char *s = lua_tolstring(L, -1, &vl); + if (vl <= bufffree(B)) { /* fit into buffer? */ + memcpy(B->p, s, vl); /* put it there */ + B->p += vl; + lua_pop(L, 1); /* remove from stack */ + } + else { + if (emptybuffer(B)) + lua_insert(L, -2); /* put buffer before new value */ + B->lvl++; /* add new value into B stack */ + adjuststack(B); + } +} + + +LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) { + B->L = L; + B->p = B->buffer; + B->lvl = 0; +} + +/* }====================================================== */ + + +LUALIB_API int luaL_ref (lua_State *L, int t) { + int ref; + t = abs_index(L, t); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); /* remove from stack */ + return LUA_REFNIL; /* `nil' has a unique fixed reference */ + } + lua_rawgeti(L, t, FREELIST_REF); /* get first free element */ + ref = (int)lua_tointeger(L, -1); /* ref = t[FREELIST_REF] */ + lua_pop(L, 1); /* remove it from stack */ + if (ref != 0) { /* any free element? */ + lua_rawgeti(L, t, ref); /* remove it from list */ + lua_rawseti(L, t, FREELIST_REF); /* (t[FREELIST_REF] = t[ref]) */ + } + else { /* no free elements */ + ref = (int)lua_objlen(L, t); + ref++; /* create new reference */ + } + lua_rawseti(L, t, ref); + return ref; +} + + +LUALIB_API void luaL_unref (lua_State *L, int t, int ref) { + if (ref >= 0) { + t = abs_index(L, t); + lua_rawgeti(L, t, FREELIST_REF); + lua_rawseti(L, t, ref); /* t[ref] = t[FREELIST_REF] */ + lua_pushinteger(L, ref); + lua_rawseti(L, t, FREELIST_REF); /* t[FREELIST_REF] = ref */ + } +} + + + +/* +** {====================================================== +** Load functions +** ======================================================= +*/ + +typedef struct LoadF { + int extraline; + FILE *f; + char buff[LUAL_BUFFERSIZE]; +} LoadF; + + +static const char *getF (lua_State *L, void *ud, size_t *size) { + LoadF *lf = (LoadF *)ud; + (void)L; + if (lf->extraline) { + lf->extraline = 0; + *size = 1; + return "\n"; + } + if (feof(lf->f)) return NULL; + *size = fread(lf->buff, 1, sizeof(lf->buff), lf->f); + return (*size > 0) ? lf->buff : NULL; +} + + +static int errfile (lua_State *L, const char *what, int fnameindex) { + const char *serr = strerror(errno); + const char *filename = lua_tostring(L, fnameindex) + 1; + lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr); + lua_remove(L, fnameindex); + return LUA_ERRFILE; +} + + +LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) { + LoadF lf; + int status, readstatus; + int c; + int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */ + lf.extraline = 0; + if (filename == NULL) { + lua_pushliteral(L, "=stdin"); + lf.f = stdin; + } + else { + lua_pushfstring(L, "@%s", filename); + lf.f = fopen(filename, "r"); + if (lf.f == NULL) return errfile(L, "open", fnameindex); + } + c = getc(lf.f); + if (c == '#') { /* Unix exec. file? */ + lf.extraline = 1; + while ((c = getc(lf.f)) != EOF && c != '\n') ; /* skip first line */ + if (c == '\n') c = getc(lf.f); + } + if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */ + lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */ + if (lf.f == NULL) return errfile(L, "reopen", fnameindex); + /* skip eventual `#!...' */ + while ((c = getc(lf.f)) != EOF && c != LUA_SIGNATURE[0]) ; + lf.extraline = 0; + } + ungetc(c, lf.f); + status = lua_load(L, getF, &lf, lua_tostring(L, -1)); + readstatus = ferror(lf.f); + if (filename) fclose(lf.f); /* close file (even in case of errors) */ + if (readstatus) { + lua_settop(L, fnameindex); /* ignore results from `lua_load' */ + return errfile(L, "read", fnameindex); + } + lua_remove(L, fnameindex); + return status; +} + + +typedef struct LoadS { + const char *s; + size_t size; +} LoadS; + + +static const char *getS (lua_State *L, void *ud, size_t *size) { + LoadS *ls = (LoadS *)ud; + (void)L; + if (ls->size == 0) return NULL; + *size = ls->size; + ls->size = 0; + return ls->s; +} + + +LUALIB_API int luaL_loadbuffer (lua_State *L, const char *buff, size_t size, + const char *name) { + LoadS ls; + ls.s = buff; + ls.size = size; + return lua_load(L, getS, &ls, name); +} + + +LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s) { + return luaL_loadbuffer(L, s, strlen(s), s); +} + + + +/* }====================================================== */ + + +static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) { + (void)ud; + (void)osize; + if (nsize == 0) { + free(ptr); + return NULL; + } + else + return realloc(ptr, nsize); +} + + +static int panic (lua_State *L) { + (void)L; /* to avoid warnings */ + fprintf(stderr, "PANIC: unprotected error in call to Lua API (%s)\n", + lua_tostring(L, -1)); + return 0; +} + + +LUALIB_API lua_State *luaL_newstate (void) { + lua_State *L = lua_newstate(l_alloc, NULL); + if (L) lua_atpanic(L, &panic); + return L; +} + diff --git a/lib/lua/lauxlib.h b/lib/lua/lauxlib.h new file mode 100644 index 000000000000..34258235dbeb --- /dev/null +++ b/lib/lua/lauxlib.h @@ -0,0 +1,174 @@ +/* +** $Id: lauxlib.h,v 1.88.1.1 2007/12/27 13:02:25 roberto Exp $ +** Auxiliary functions for building Lua libraries +** See Copyright Notice in lua.h +*/ + + +#ifndef lauxlib_h +#define lauxlib_h + + +#include +#include + +#include "lua.h" + + +#if defined(LUA_COMPAT_GETN) +LUALIB_API int (luaL_getn) (lua_State *L, int t); +LUALIB_API void (luaL_setn) (lua_State *L, int t, int n); +#else +#define luaL_getn(L,i) ((int)lua_objlen(L, i)) +#define luaL_setn(L,i,j) ((void)0) /* no op! */ +#endif + +#if defined(LUA_COMPAT_OPENLIB) +#define luaI_openlib luaL_openlib +#endif + + +/* extra error code for `luaL_load' */ +#define LUA_ERRFILE (LUA_ERRERR+1) + + +typedef struct luaL_Reg { + const char *name; + lua_CFunction func; +} luaL_Reg; + + + +LUALIB_API void (luaI_openlib) (lua_State *L, const char *libname, + const luaL_Reg *l, int nup); +LUALIB_API void (luaL_register) (lua_State *L, const char *libname, + const luaL_Reg *l); +LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e); +LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e); +LUALIB_API int (luaL_typerror) (lua_State *L, int narg, const char *tname); +LUALIB_API int (luaL_argerror) (lua_State *L, int numarg, const char *extramsg); +LUALIB_API const char *(luaL_checklstring) (lua_State *L, int numArg, + size_t *l); +LUALIB_API const char *(luaL_optlstring) (lua_State *L, int numArg, + const char *def, size_t *l); +LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int numArg); +LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int nArg, lua_Number def); + +LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int numArg); +LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int nArg, + lua_Integer def); + +LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg); +LUALIB_API void (luaL_checktype) (lua_State *L, int narg, int t); +LUALIB_API void (luaL_checkany) (lua_State *L, int narg); + +LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname); +LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname); + +LUALIB_API void (luaL_where) (lua_State *L, int lvl); +LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...); + +LUALIB_API int (luaL_checkoption) (lua_State *L, int narg, const char *def, + const char *const lst[]); + +LUALIB_API int (luaL_ref) (lua_State *L, int t); +LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref); + +LUALIB_API int (luaL_loadfile) (lua_State *L, const char *filename); +LUALIB_API int (luaL_loadbuffer) (lua_State *L, const char *buff, size_t sz, + const char *name); +LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s); + +LUALIB_API lua_State *(luaL_newstate) (void); + + +LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p, + const char *r); + +LUALIB_API const char *(luaL_findtable) (lua_State *L, int idx, + const char *fname, int szhint); + + + + +/* +** =============================================================== +** some useful macros +** =============================================================== +*/ + +#define luaL_argcheck(L, cond,numarg,extramsg) \ + ((void)((cond) || luaL_argerror(L, (numarg), (extramsg)))) +#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL)) +#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL)) +#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n))) +#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d))) +#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n))) +#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d))) + +#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i))) + +#define luaL_dofile(L, fn) \ + (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0)) + +#define luaL_dostring(L, s) \ + (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0)) + +#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n))) + +#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n))) + +/* +** {====================================================== +** Generic Buffer manipulation +** ======================================================= +*/ + + + +typedef struct luaL_Buffer { + char *p; /* current position in buffer */ + int lvl; /* number of strings in the stack (level) */ + lua_State *L; + char buffer[LUAL_BUFFERSIZE]; +} luaL_Buffer; + +#define luaL_addchar(B,c) \ + ((void)((B)->p < ((B)->buffer+LUAL_BUFFERSIZE) || luaL_prepbuffer(B)), \ + (*(B)->p++ = (char)(c))) + +/* compatibility only */ +#define luaL_putchar(B,c) luaL_addchar(B,c) + +#define luaL_addsize(B,n) ((B)->p += (n)) + +LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B); +LUALIB_API char *(luaL_prepbuffer) (luaL_Buffer *B); +LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l); +LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s); +LUALIB_API void (luaL_addvalue) (luaL_Buffer *B); +LUALIB_API void (luaL_pushresult) (luaL_Buffer *B); + + +/* }====================================================== */ + + +/* compatibility with ref system */ + +/* pre-defined references */ +#define LUA_NOREF (-2) +#define LUA_REFNIL (-1) + +#define lua_ref(L,lock) ((lock) ? luaL_ref(L, LUA_REGISTRYINDEX) : \ + (lua_pushstring(L, "unlocked references are obsolete"), lua_error(L), 0)) + +#define lua_unref(L,ref) luaL_unref(L, LUA_REGISTRYINDEX, (ref)) + +#define lua_getref(L,ref) lua_rawgeti(L, LUA_REGISTRYINDEX, (ref)) + + +#define luaL_reg luaL_Reg + +#endif + + diff --git a/lib/lua/lbaselib.c b/lib/lua/lbaselib.c new file mode 100644 index 000000000000..2ab550bd48de --- /dev/null +++ b/lib/lua/lbaselib.c @@ -0,0 +1,653 @@ +/* +** $Id: lbaselib.c,v 1.191.1.6 2008/02/14 16:46:22 roberto Exp $ +** Basic library +** See Copyright Notice in lua.h +*/ + + + +#include +#include +#include +#include + +#define lbaselib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + + + +/* +** If your system does not support `stdout', you can just remove this function. +** If you need, you can define your own `print' function, following this +** model but changing `fputs' to put the strings at a proper place +** (a console window or a log file, for instance). +*/ +static int luaB_print (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + int i; + lua_getglobal(L, "tostring"); + for (i=1; i<=n; i++) { + const char *s; + lua_pushvalue(L, -1); /* function to be called */ + lua_pushvalue(L, i); /* value to print */ + lua_call(L, 1, 1); + s = lua_tostring(L, -1); /* get result */ + if (s == NULL) + return luaL_error(L, LUA_QL("tostring") " must return a string to " + LUA_QL("print")); + if (i>1) fputs("\t", stdout); + fputs(s, stdout); + lua_pop(L, 1); /* pop result */ + } + fputs("\n", stdout); + return 0; +} + + +static int luaB_tonumber (lua_State *L) { + int base = luaL_optint(L, 2, 10); + if (base == 10) { /* standard conversion */ + luaL_checkany(L, 1); + if (lua_isnumber(L, 1)) { + lua_pushnumber(L, lua_tonumber(L, 1)); + return 1; + } + } + else { + const char *s1 = luaL_checkstring(L, 1); + char *s2; + unsigned long n; + luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range"); + n = strtoul(s1, &s2, base); + if (s1 != s2) { /* at least one valid digit? */ + while (isspace((unsigned char)(*s2))) s2++; /* skip trailing spaces */ + if (*s2 == '\0') { /* no invalid trailing characters? */ + lua_pushnumber(L, (lua_Number)n); + return 1; + } + } + } + lua_pushnil(L); /* else not a number */ + return 1; +} + + +static int luaB_error (lua_State *L) { + int level = luaL_optint(L, 2, 1); + lua_settop(L, 1); + if (lua_isstring(L, 1) && level > 0) { /* add extra information? */ + luaL_where(L, level); + lua_pushvalue(L, 1); + lua_concat(L, 2); + } + return lua_error(L); +} + + +static int luaB_getmetatable (lua_State *L) { + luaL_checkany(L, 1); + if (!lua_getmetatable(L, 1)) { + lua_pushnil(L); + return 1; /* no metatable */ + } + luaL_getmetafield(L, 1, "__metatable"); + return 1; /* returns either __metatable field (if present) or metatable */ +} + + +static int luaB_setmetatable (lua_State *L) { + int t = lua_type(L, 2); + luaL_checktype(L, 1, LUA_TTABLE); + luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2, + "nil or table expected"); + if (luaL_getmetafield(L, 1, "__metatable")) + luaL_error(L, "cannot change a protected metatable"); + lua_settop(L, 2); + lua_setmetatable(L, 1); + return 1; +} + + +static void getfunc (lua_State *L, int opt) { + if (lua_isfunction(L, 1)) lua_pushvalue(L, 1); + else { + lua_Debug ar; + int level = opt ? luaL_optint(L, 1, 1) : luaL_checkint(L, 1); + luaL_argcheck(L, level >= 0, 1, "level must be non-negative"); + if (lua_getstack(L, level, &ar) == 0) + luaL_argerror(L, 1, "invalid level"); + lua_getinfo(L, "f", &ar); + if (lua_isnil(L, -1)) + luaL_error(L, "no function environment for tail call at level %d", + level); + } +} + + +static int luaB_getfenv (lua_State *L) { + getfunc(L, 1); + if (lua_iscfunction(L, -1)) /* is a C function? */ + lua_pushvalue(L, LUA_GLOBALSINDEX); /* return the thread's global env. */ + else + lua_getfenv(L, -1); + return 1; +} + + +static int luaB_setfenv (lua_State *L) { + luaL_checktype(L, 2, LUA_TTABLE); + getfunc(L, 0); + lua_pushvalue(L, 2); + if (lua_isnumber(L, 1) && lua_tonumber(L, 1) == 0) { + /* change environment of current thread */ + lua_pushthread(L); + lua_insert(L, -2); + lua_setfenv(L, -2); + return 0; + } + else if (lua_iscfunction(L, -2) || lua_setfenv(L, -2) == 0) + luaL_error(L, + LUA_QL("setfenv") " cannot change environment of given object"); + return 1; +} + + +static int luaB_rawequal (lua_State *L) { + luaL_checkany(L, 1); + luaL_checkany(L, 2); + lua_pushboolean(L, lua_rawequal(L, 1, 2)); + return 1; +} + + +static int luaB_rawget (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checkany(L, 2); + lua_settop(L, 2); + lua_rawget(L, 1); + return 1; +} + +static int luaB_rawset (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checkany(L, 2); + luaL_checkany(L, 3); + lua_settop(L, 3); + lua_rawset(L, 1); + return 1; +} + + +static int luaB_gcinfo (lua_State *L) { + lua_pushinteger(L, lua_getgccount(L)); + return 1; +} + + +static int luaB_collectgarbage (lua_State *L) { + static const char *const opts[] = {"stop", "restart", "collect", + "count", "step", "setpause", "setstepmul", NULL}; + static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT, + LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL}; + int o = luaL_checkoption(L, 1, "collect", opts); + int ex = luaL_optint(L, 2, 0); + int res = lua_gc(L, optsnum[o], ex); + switch (optsnum[o]) { + case LUA_GCCOUNT: { + int b = lua_gc(L, LUA_GCCOUNTB, 0); + lua_pushnumber(L, res + ((lua_Number)b/1024)); + return 1; + } + case LUA_GCSTEP: { + lua_pushboolean(L, res); + return 1; + } + default: { + lua_pushnumber(L, res); + return 1; + } + } +} + + +static int luaB_type (lua_State *L) { + luaL_checkany(L, 1); + lua_pushstring(L, luaL_typename(L, 1)); + return 1; +} + + +static int luaB_next (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + lua_settop(L, 2); /* create a 2nd argument if there isn't one */ + if (lua_next(L, 1)) + return 2; + else { + lua_pushnil(L); + return 1; + } +} + + +static int luaB_pairs (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + lua_pushvalue(L, lua_upvalueindex(1)); /* return generator, */ + lua_pushvalue(L, 1); /* state, */ + lua_pushnil(L); /* and initial value */ + return 3; +} + + +static int ipairsaux (lua_State *L) { + int i = luaL_checkint(L, 2); + luaL_checktype(L, 1, LUA_TTABLE); + i++; /* next value */ + lua_pushinteger(L, i); + lua_rawgeti(L, 1, i); + return (lua_isnil(L, -1)) ? 0 : 2; +} + + +static int luaB_ipairs (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + lua_pushvalue(L, lua_upvalueindex(1)); /* return generator, */ + lua_pushvalue(L, 1); /* state, */ + lua_pushinteger(L, 0); /* and initial value */ + return 3; +} + + +static int load_aux (lua_State *L, int status) { + if (status == 0) /* OK? */ + return 1; + else { + lua_pushnil(L); + lua_insert(L, -2); /* put before error message */ + return 2; /* return nil plus error message */ + } +} + + +static int luaB_loadstring (lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + const char *chunkname = luaL_optstring(L, 2, s); + return load_aux(L, luaL_loadbuffer(L, s, l, chunkname)); +} + + +static int luaB_loadfile (lua_State *L) { + const char *fname = luaL_optstring(L, 1, NULL); + return load_aux(L, luaL_loadfile(L, fname)); +} + + +/* +** Reader for generic `load' function: `lua_load' uses the +** stack for internal stuff, so the reader cannot change the +** stack top. Instead, it keeps its resulting string in a +** reserved slot inside the stack. +*/ +static const char *generic_reader (lua_State *L, void *ud, size_t *size) { + (void)ud; /* to avoid warnings */ + luaL_checkstack(L, 2, "too many nested functions"); + lua_pushvalue(L, 1); /* get function */ + lua_call(L, 0, 1); /* call it */ + if (lua_isnil(L, -1)) { + *size = 0; + return NULL; + } + else if (lua_isstring(L, -1)) { + lua_replace(L, 3); /* save string in a reserved stack slot */ + return lua_tolstring(L, 3, size); + } + else luaL_error(L, "reader function must return a string"); + return NULL; /* to avoid warnings */ +} + + +static int luaB_load (lua_State *L) { + int status; + const char *cname = luaL_optstring(L, 2, "=(load)"); + luaL_checktype(L, 1, LUA_TFUNCTION); + lua_settop(L, 3); /* function, eventual name, plus one reserved slot */ + status = lua_load(L, generic_reader, NULL, cname); + return load_aux(L, status); +} + + +static int luaB_dofile (lua_State *L) { + const char *fname = luaL_optstring(L, 1, NULL); + int n = lua_gettop(L); + if (luaL_loadfile(L, fname) != 0) lua_error(L); + lua_call(L, 0, LUA_MULTRET); + return lua_gettop(L) - n; +} + + +static int luaB_assert (lua_State *L) { + luaL_checkany(L, 1); + if (!lua_toboolean(L, 1)) + return luaL_error(L, "%s", luaL_optstring(L, 2, "assertion failed!")); + return lua_gettop(L); +} + + +static int luaB_unpack (lua_State *L) { + int i, e, n; + luaL_checktype(L, 1, LUA_TTABLE); + i = luaL_optint(L, 2, 1); + e = luaL_opt(L, luaL_checkint, 3, luaL_getn(L, 1)); + if (i > e) return 0; /* empty range */ + n = e - i + 1; /* number of elements */ + if (n <= 0 || !lua_checkstack(L, n)) /* n <= 0 means arith. overflow */ + return luaL_error(L, "too many results to unpack"); + lua_rawgeti(L, 1, i); /* push arg[i] (avoiding overflow problems) */ + while (i++ < e) /* push arg[i + 1...e] */ + lua_rawgeti(L, 1, i); + return n; +} + + +static int luaB_select (lua_State *L) { + int n = lua_gettop(L); + if (lua_type(L, 1) == LUA_TSTRING && *lua_tostring(L, 1) == '#') { + lua_pushinteger(L, n-1); + return 1; + } + else { + int i = luaL_checkint(L, 1); + if (i < 0) i = n + i; + else if (i > n) i = n; + luaL_argcheck(L, 1 <= i, 1, "index out of range"); + return n - i; + } +} + + +static int luaB_pcall (lua_State *L) { + int status; + luaL_checkany(L, 1); + status = lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0); + lua_pushboolean(L, (status == 0)); + lua_insert(L, 1); + return lua_gettop(L); /* return status + all results */ +} + + +static int luaB_xpcall (lua_State *L) { + int status; + luaL_checkany(L, 2); + lua_settop(L, 2); + lua_insert(L, 1); /* put error function under function to be called */ + status = lua_pcall(L, 0, LUA_MULTRET, 1); + lua_pushboolean(L, (status == 0)); + lua_replace(L, 1); + return lua_gettop(L); /* return status + all results */ +} + + +static int luaB_tostring (lua_State *L) { + luaL_checkany(L, 1); + if (luaL_callmeta(L, 1, "__tostring")) /* is there a metafield? */ + return 1; /* use its value */ + switch (lua_type(L, 1)) { + case LUA_TNUMBER: + lua_pushstring(L, lua_tostring(L, 1)); + break; + case LUA_TSTRING: + lua_pushvalue(L, 1); + break; + case LUA_TBOOLEAN: + lua_pushstring(L, (lua_toboolean(L, 1) ? "true" : "false")); + break; + case LUA_TNIL: + lua_pushliteral(L, "nil"); + break; + default: + lua_pushfstring(L, "%s: %p", luaL_typename(L, 1), lua_topointer(L, 1)); + break; + } + return 1; +} + + +static int luaB_newproxy (lua_State *L) { + lua_settop(L, 1); + lua_newuserdata(L, 0); /* create proxy */ + if (lua_toboolean(L, 1) == 0) + return 1; /* no metatable */ + else if (lua_isboolean(L, 1)) { + lua_newtable(L); /* create a new metatable `m' ... */ + lua_pushvalue(L, -1); /* ... and mark `m' as a valid metatable */ + lua_pushboolean(L, 1); + lua_rawset(L, lua_upvalueindex(1)); /* weaktable[m] = true */ + } + else { + int validproxy = 0; /* to check if weaktable[metatable(u)] == true */ + if (lua_getmetatable(L, 1)) { + lua_rawget(L, lua_upvalueindex(1)); + validproxy = lua_toboolean(L, -1); + lua_pop(L, 1); /* remove value */ + } + luaL_argcheck(L, validproxy, 1, "boolean or proxy expected"); + lua_getmetatable(L, 1); /* metatable is valid; get it */ + } + lua_setmetatable(L, 2); + return 1; +} + + +static const luaL_Reg base_funcs[] = { + {"assert", luaB_assert}, + {"collectgarbage", luaB_collectgarbage}, + {"dofile", luaB_dofile}, + {"error", luaB_error}, + {"gcinfo", luaB_gcinfo}, + {"getfenv", luaB_getfenv}, + {"getmetatable", luaB_getmetatable}, + {"loadfile", luaB_loadfile}, + {"load", luaB_load}, + {"loadstring", luaB_loadstring}, + {"next", luaB_next}, + {"pcall", luaB_pcall}, + {"print", luaB_print}, + {"rawequal", luaB_rawequal}, + {"rawget", luaB_rawget}, + {"rawset", luaB_rawset}, + {"select", luaB_select}, + {"setfenv", luaB_setfenv}, + {"setmetatable", luaB_setmetatable}, + {"tonumber", luaB_tonumber}, + {"tostring", luaB_tostring}, + {"type", luaB_type}, + {"unpack", luaB_unpack}, + {"xpcall", luaB_xpcall}, + {NULL, NULL} +}; + + +/* +** {====================================================== +** Coroutine library +** ======================================================= +*/ + +#define CO_RUN 0 /* running */ +#define CO_SUS 1 /* suspended */ +#define CO_NOR 2 /* 'normal' (it resumed another coroutine) */ +#define CO_DEAD 3 + +static const char *const statnames[] = + {"running", "suspended", "normal", "dead"}; + +static int costatus (lua_State *L, lua_State *co) { + if (L == co) return CO_RUN; + switch (lua_status(co)) { + case LUA_YIELD: + return CO_SUS; + case 0: { + lua_Debug ar; + if (lua_getstack(co, 0, &ar) > 0) /* does it have frames? */ + return CO_NOR; /* it is running */ + else if (lua_gettop(co) == 0) + return CO_DEAD; + else + return CO_SUS; /* initial state */ + } + default: /* some error occured */ + return CO_DEAD; + } +} + + +static int luaB_costatus (lua_State *L) { + lua_State *co = lua_tothread(L, 1); + luaL_argcheck(L, co, 1, "coroutine expected"); + lua_pushstring(L, statnames[costatus(L, co)]); + return 1; +} + + +static int auxresume (lua_State *L, lua_State *co, int narg) { + int status = costatus(L, co); + if (!lua_checkstack(co, narg)) + luaL_error(L, "too many arguments to resume"); + if (status != CO_SUS) { + lua_pushfstring(L, "cannot resume %s coroutine", statnames[status]); + return -1; /* error flag */ + } + lua_xmove(L, co, narg); + lua_setlevel(L, co); + status = lua_resume(co, narg); + if (status == 0 || status == LUA_YIELD) { + int nres = lua_gettop(co); + if (!lua_checkstack(L, nres + 1)) + luaL_error(L, "too many results to resume"); + lua_xmove(co, L, nres); /* move yielded values */ + return nres; + } + else { + lua_xmove(co, L, 1); /* move error message */ + return -1; /* error flag */ + } +} + + +static int luaB_coresume (lua_State *L) { + lua_State *co = lua_tothread(L, 1); + int r; + luaL_argcheck(L, co, 1, "coroutine expected"); + r = auxresume(L, co, lua_gettop(L) - 1); + if (r < 0) { + lua_pushboolean(L, 0); + lua_insert(L, -2); + return 2; /* return false + error message */ + } + else { + lua_pushboolean(L, 1); + lua_insert(L, -(r + 1)); + return r + 1; /* return true + `resume' returns */ + } +} + + +static int luaB_auxwrap (lua_State *L) { + lua_State *co = lua_tothread(L, lua_upvalueindex(1)); + int r = auxresume(L, co, lua_gettop(L)); + if (r < 0) { + if (lua_isstring(L, -1)) { /* error object is a string? */ + luaL_where(L, 1); /* add extra info */ + lua_insert(L, -2); + lua_concat(L, 2); + } + lua_error(L); /* propagate error */ + } + return r; +} + + +static int luaB_cocreate (lua_State *L) { + lua_State *NL = lua_newthread(L); + luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1, + "Lua function expected"); + lua_pushvalue(L, 1); /* move function to top */ + lua_xmove(L, NL, 1); /* move function from L to NL */ + return 1; +} + + +static int luaB_cowrap (lua_State *L) { + luaB_cocreate(L); + lua_pushcclosure(L, luaB_auxwrap, 1); + return 1; +} + + +static int luaB_yield (lua_State *L) { + return lua_yield(L, lua_gettop(L)); +} + + +static int luaB_corunning (lua_State *L) { + if (lua_pushthread(L)) + lua_pushnil(L); /* main thread is not a coroutine */ + return 1; +} + + +static const luaL_Reg co_funcs[] = { + {"create", luaB_cocreate}, + {"resume", luaB_coresume}, + {"running", luaB_corunning}, + {"status", luaB_costatus}, + {"wrap", luaB_cowrap}, + {"yield", luaB_yield}, + {NULL, NULL} +}; + +/* }====================================================== */ + + +static void auxopen (lua_State *L, const char *name, + lua_CFunction f, lua_CFunction u) { + lua_pushcfunction(L, u); + lua_pushcclosure(L, f, 1); + lua_setfield(L, -2, name); +} + + +static void base_open (lua_State *L) { + /* set global _G */ + lua_pushvalue(L, LUA_GLOBALSINDEX); + lua_setglobal(L, "_G"); + /* open lib into global table */ + luaL_register(L, "_G", base_funcs); + lua_pushliteral(L, LUA_VERSION); + lua_setglobal(L, "_VERSION"); /* set global _VERSION */ + /* `ipairs' and `pairs' need auxiliary functions as upvalues */ + auxopen(L, "ipairs", luaB_ipairs, ipairsaux); + auxopen(L, "pairs", luaB_pairs, luaB_next); + /* `newproxy' needs a weaktable as upvalue */ + lua_createtable(L, 0, 1); /* new table `w' */ + lua_pushvalue(L, -1); /* `w' will be its own metatable */ + lua_setmetatable(L, -2); + lua_pushliteral(L, "kv"); + lua_setfield(L, -2, "__mode"); /* metatable(w).__mode = "kv" */ + lua_pushcclosure(L, luaB_newproxy, 1); + lua_setglobal(L, "newproxy"); /* set global `newproxy' */ +} + + +LUALIB_API int luaopen_base (lua_State *L) { + base_open(L); + luaL_register(L, LUA_COLIBNAME, co_funcs); + return 2; +} + diff --git a/lib/lua/lcode.c b/lib/lua/lcode.c new file mode 100644 index 000000000000..679cb9cfd98b --- /dev/null +++ b/lib/lua/lcode.c @@ -0,0 +1,831 @@ +/* +** $Id: lcode.c,v 2.25.1.5 2011/01/31 14:53:16 roberto Exp $ +** Code generator for Lua +** See Copyright Notice in lua.h +*/ + + +#include + +#define lcode_c +#define LUA_CORE + +#include "lua.h" + +#include "lcode.h" +#include "ldebug.h" +#include "ldo.h" +#include "lgc.h" +#include "llex.h" +#include "lmem.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lparser.h" +#include "ltable.h" + + +#define hasjumps(e) ((e)->t != (e)->f) + + +static int isnumeral(expdesc *e) { + return (e->k == VKNUM && e->t == NO_JUMP && e->f == NO_JUMP); +} + + +void luaK_nil (FuncState *fs, int from, int n) { + Instruction *previous; + if (fs->pc > fs->lasttarget) { /* no jumps to current position? */ + if (fs->pc == 0) { /* function start? */ + if (from >= fs->nactvar) + return; /* positions are already clean */ + } + else { + previous = &fs->f->code[fs->pc-1]; + if (GET_OPCODE(*previous) == OP_LOADNIL) { + int pfrom = GETARG_A(*previous); + int pto = GETARG_B(*previous); + if (pfrom <= from && from <= pto+1) { /* can connect both? */ + if (from+n-1 > pto) + SETARG_B(*previous, from+n-1); + return; + } + } + } + } + luaK_codeABC(fs, OP_LOADNIL, from, from+n-1, 0); /* else no optimization */ +} + + +int luaK_jump (FuncState *fs) { + int jpc = fs->jpc; /* save list of jumps to here */ + int j; + fs->jpc = NO_JUMP; + j = luaK_codeAsBx(fs, OP_JMP, 0, NO_JUMP); + luaK_concat(fs, &j, jpc); /* keep them on hold */ + return j; +} + + +void luaK_ret (FuncState *fs, int first, int nret) { + luaK_codeABC(fs, OP_RETURN, first, nret+1, 0); +} + + +static int condjump (FuncState *fs, OpCode op, int A, int B, int C) { + luaK_codeABC(fs, op, A, B, C); + return luaK_jump(fs); +} + + +static void fixjump (FuncState *fs, int pc, int dest) { + Instruction *jmp = &fs->f->code[pc]; + int offset = dest-(pc+1); + lua_assert(dest != NO_JUMP); + if (abs(offset) > MAXARG_sBx) + luaX_syntaxerror(fs->ls, "control structure too long"); + SETARG_sBx(*jmp, offset); +} + + +/* +** returns current `pc' and marks it as a jump target (to avoid wrong +** optimizations with consecutive instructions not in the same basic block). +*/ +int luaK_getlabel (FuncState *fs) { + fs->lasttarget = fs->pc; + return fs->pc; +} + + +static int getjump (FuncState *fs, int pc) { + int offset = GETARG_sBx(fs->f->code[pc]); + if (offset == NO_JUMP) /* point to itself represents end of list */ + return NO_JUMP; /* end of list */ + else + return (pc+1)+offset; /* turn offset into absolute position */ +} + + +static Instruction *getjumpcontrol (FuncState *fs, int pc) { + Instruction *pi = &fs->f->code[pc]; + if (pc >= 1 && testTMode(GET_OPCODE(*(pi-1)))) + return pi-1; + else + return pi; +} + + +/* +** check whether list has any jump that do not produce a value +** (or produce an inverted value) +*/ +static int need_value (FuncState *fs, int list) { + for (; list != NO_JUMP; list = getjump(fs, list)) { + Instruction i = *getjumpcontrol(fs, list); + if (GET_OPCODE(i) != OP_TESTSET) return 1; + } + return 0; /* not found */ +} + + +static int patchtestreg (FuncState *fs, int node, int reg) { + Instruction *i = getjumpcontrol(fs, node); + if (GET_OPCODE(*i) != OP_TESTSET) + return 0; /* cannot patch other instructions */ + if (reg != NO_REG && reg != GETARG_B(*i)) + SETARG_A(*i, reg); + else /* no register to put value or register already has the value */ + *i = CREATE_ABC(OP_TEST, GETARG_B(*i), 0, GETARG_C(*i)); + + return 1; +} + + +static void removevalues (FuncState *fs, int list) { + for (; list != NO_JUMP; list = getjump(fs, list)) + patchtestreg(fs, list, NO_REG); +} + + +static void patchlistaux (FuncState *fs, int list, int vtarget, int reg, + int dtarget) { + while (list != NO_JUMP) { + int next = getjump(fs, list); + if (patchtestreg(fs, list, reg)) + fixjump(fs, list, vtarget); + else + fixjump(fs, list, dtarget); /* jump to default target */ + list = next; + } +} + + +static void dischargejpc (FuncState *fs) { + patchlistaux(fs, fs->jpc, fs->pc, NO_REG, fs->pc); + fs->jpc = NO_JUMP; +} + + +void luaK_patchlist (FuncState *fs, int list, int target) { + if (target == fs->pc) + luaK_patchtohere(fs, list); + else { + lua_assert(target < fs->pc); + patchlistaux(fs, list, target, NO_REG, target); + } +} + + +void luaK_patchtohere (FuncState *fs, int list) { + luaK_getlabel(fs); + luaK_concat(fs, &fs->jpc, list); +} + + +void luaK_concat (FuncState *fs, int *l1, int l2) { + if (l2 == NO_JUMP) return; + else if (*l1 == NO_JUMP) + *l1 = l2; + else { + int list = *l1; + int next; + while ((next = getjump(fs, list)) != NO_JUMP) /* find last element */ + list = next; + fixjump(fs, list, l2); + } +} + + +void luaK_checkstack (FuncState *fs, int n) { + int newstack = fs->freereg + n; + if (newstack > fs->f->maxstacksize) { + if (newstack >= MAXSTACK) + luaX_syntaxerror(fs->ls, "function or expression too complex"); + fs->f->maxstacksize = cast_byte(newstack); + } +} + + +void luaK_reserveregs (FuncState *fs, int n) { + luaK_checkstack(fs, n); + fs->freereg += n; +} + + +static void freereg (FuncState *fs, int reg) { + if (!ISK(reg) && reg >= fs->nactvar) { + fs->freereg--; + lua_assert(reg == fs->freereg); + } +} + + +static void freeexp (FuncState *fs, expdesc *e) { + if (e->k == VNONRELOC) + freereg(fs, e->u.s.info); +} + + +static int addk (FuncState *fs, TValue *k, TValue *v) { + lua_State *L = fs->L; + TValue *idx = luaH_set(L, fs->h, k); + Proto *f = fs->f; + int oldsize = f->sizek; + if (ttisnumber(idx)) { + lua_assert(luaO_rawequalObj(&fs->f->k[cast_int(nvalue(idx))], v)); + return cast_int(nvalue(idx)); + } + else { /* constant not found; create a new entry */ + setnvalue(idx, cast_num(fs->nk)); + luaM_growvector(L, f->k, fs->nk, f->sizek, TValue, + MAXARG_Bx, "constant table overflow"); + while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]); + setobj(L, &f->k[fs->nk], v); + luaC_barrier(L, f, v); + return fs->nk++; + } +} + + +int luaK_stringK (FuncState *fs, TString *s) { + TValue o; + setsvalue(fs->L, &o, s); + return addk(fs, &o, &o); +} + + +int luaK_numberK (FuncState *fs, lua_Number r) { + TValue o; + setnvalue(&o, r); + return addk(fs, &o, &o); +} + + +static int boolK (FuncState *fs, int b) { + TValue o; + setbvalue(&o, b); + return addk(fs, &o, &o); +} + + +static int nilK (FuncState *fs) { + TValue k, v; + setnilvalue(&v); + /* cannot use nil as key; instead use table itself to represent nil */ + sethvalue(fs->L, &k, fs->h); + return addk(fs, &k, &v); +} + + +void luaK_setreturns (FuncState *fs, expdesc *e, int nresults) { + if (e->k == VCALL) { /* expression is an open function call? */ + SETARG_C(getcode(fs, e), nresults+1); + } + else if (e->k == VVARARG) { + SETARG_B(getcode(fs, e), nresults+1); + SETARG_A(getcode(fs, e), fs->freereg); + luaK_reserveregs(fs, 1); + } +} + + +void luaK_setoneret (FuncState *fs, expdesc *e) { + if (e->k == VCALL) { /* expression is an open function call? */ + e->k = VNONRELOC; + e->u.s.info = GETARG_A(getcode(fs, e)); + } + else if (e->k == VVARARG) { + SETARG_B(getcode(fs, e), 2); + e->k = VRELOCABLE; /* can relocate its simple result */ + } +} + + +void luaK_dischargevars (FuncState *fs, expdesc *e) { + switch (e->k) { + case VLOCAL: { + e->k = VNONRELOC; + break; + } + case VUPVAL: { + e->u.s.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.s.info, 0); + e->k = VRELOCABLE; + break; + } + case VGLOBAL: { + e->u.s.info = luaK_codeABx(fs, OP_GETGLOBAL, 0, e->u.s.info); + e->k = VRELOCABLE; + break; + } + case VINDEXED: { + freereg(fs, e->u.s.aux); + freereg(fs, e->u.s.info); + e->u.s.info = luaK_codeABC(fs, OP_GETTABLE, 0, e->u.s.info, e->u.s.aux); + e->k = VRELOCABLE; + break; + } + case VVARARG: + case VCALL: { + luaK_setoneret(fs, e); + break; + } + default: break; /* there is one value available (somewhere) */ + } +} + + +static int code_label (FuncState *fs, int A, int b, int jump) { + luaK_getlabel(fs); /* those instructions may be jump targets */ + return luaK_codeABC(fs, OP_LOADBOOL, A, b, jump); +} + + +static void discharge2reg (FuncState *fs, expdesc *e, int reg) { + luaK_dischargevars(fs, e); + switch (e->k) { + case VNIL: { + luaK_nil(fs, reg, 1); + break; + } + case VFALSE: case VTRUE: { + luaK_codeABC(fs, OP_LOADBOOL, reg, e->k == VTRUE, 0); + break; + } + case VK: { + luaK_codeABx(fs, OP_LOADK, reg, e->u.s.info); + break; + } + case VKNUM: { + luaK_codeABx(fs, OP_LOADK, reg, luaK_numberK(fs, e->u.nval)); + break; + } + case VRELOCABLE: { + Instruction *pc = &getcode(fs, e); + SETARG_A(*pc, reg); + break; + } + case VNONRELOC: { + if (reg != e->u.s.info) + luaK_codeABC(fs, OP_MOVE, reg, e->u.s.info, 0); + break; + } + default: { + lua_assert(e->k == VVOID || e->k == VJMP); + return; /* nothing to do... */ + } + } + e->u.s.info = reg; + e->k = VNONRELOC; +} + + +static void discharge2anyreg (FuncState *fs, expdesc *e) { + if (e->k != VNONRELOC) { + luaK_reserveregs(fs, 1); + discharge2reg(fs, e, fs->freereg-1); + } +} + + +static void exp2reg (FuncState *fs, expdesc *e, int reg) { + discharge2reg(fs, e, reg); + if (e->k == VJMP) + luaK_concat(fs, &e->t, e->u.s.info); /* put this jump in `t' list */ + if (hasjumps(e)) { + int final; /* position after whole expression */ + int p_f = NO_JUMP; /* position of an eventual LOAD false */ + int p_t = NO_JUMP; /* position of an eventual LOAD true */ + if (need_value(fs, e->t) || need_value(fs, e->f)) { + int fj = (e->k == VJMP) ? NO_JUMP : luaK_jump(fs); + p_f = code_label(fs, reg, 0, 1); + p_t = code_label(fs, reg, 1, 0); + luaK_patchtohere(fs, fj); + } + final = luaK_getlabel(fs); + patchlistaux(fs, e->f, final, reg, p_f); + patchlistaux(fs, e->t, final, reg, p_t); + } + e->f = e->t = NO_JUMP; + e->u.s.info = reg; + e->k = VNONRELOC; +} + + +void luaK_exp2nextreg (FuncState *fs, expdesc *e) { + luaK_dischargevars(fs, e); + freeexp(fs, e); + luaK_reserveregs(fs, 1); + exp2reg(fs, e, fs->freereg - 1); +} + + +int luaK_exp2anyreg (FuncState *fs, expdesc *e) { + luaK_dischargevars(fs, e); + if (e->k == VNONRELOC) { + if (!hasjumps(e)) return e->u.s.info; /* exp is already in a register */ + if (e->u.s.info >= fs->nactvar) { /* reg. is not a local? */ + exp2reg(fs, e, e->u.s.info); /* put value on it */ + return e->u.s.info; + } + } + luaK_exp2nextreg(fs, e); /* default */ + return e->u.s.info; +} + + +void luaK_exp2val (FuncState *fs, expdesc *e) { + if (hasjumps(e)) + luaK_exp2anyreg(fs, e); + else + luaK_dischargevars(fs, e); +} + + +int luaK_exp2RK (FuncState *fs, expdesc *e) { + luaK_exp2val(fs, e); + switch (e->k) { + case VKNUM: + case VTRUE: + case VFALSE: + case VNIL: { + if (fs->nk <= MAXINDEXRK) { /* constant fit in RK operand? */ + e->u.s.info = (e->k == VNIL) ? nilK(fs) : + (e->k == VKNUM) ? luaK_numberK(fs, e->u.nval) : + boolK(fs, (e->k == VTRUE)); + e->k = VK; + return RKASK(e->u.s.info); + } + else break; + } + case VK: { + if (e->u.s.info <= MAXINDEXRK) /* constant fit in argC? */ + return RKASK(e->u.s.info); + else break; + } + default: break; + } + /* not a constant in the right range: put it in a register */ + return luaK_exp2anyreg(fs, e); +} + + +void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) { + switch (var->k) { + case VLOCAL: { + freeexp(fs, ex); + exp2reg(fs, ex, var->u.s.info); + return; + } + case VUPVAL: { + int e = luaK_exp2anyreg(fs, ex); + luaK_codeABC(fs, OP_SETUPVAL, e, var->u.s.info, 0); + break; + } + case VGLOBAL: { + int e = luaK_exp2anyreg(fs, ex); + luaK_codeABx(fs, OP_SETGLOBAL, e, var->u.s.info); + break; + } + case VINDEXED: { + int e = luaK_exp2RK(fs, ex); + luaK_codeABC(fs, OP_SETTABLE, var->u.s.info, var->u.s.aux, e); + break; + } + default: { + lua_assert(0); /* invalid var kind to store */ + break; + } + } + freeexp(fs, ex); +} + + +void luaK_self (FuncState *fs, expdesc *e, expdesc *key) { + int func; + luaK_exp2anyreg(fs, e); + freeexp(fs, e); + func = fs->freereg; + luaK_reserveregs(fs, 2); + luaK_codeABC(fs, OP_SELF, func, e->u.s.info, luaK_exp2RK(fs, key)); + freeexp(fs, key); + e->u.s.info = func; + e->k = VNONRELOC; +} + + +static void invertjump (FuncState *fs, expdesc *e) { + Instruction *pc = getjumpcontrol(fs, e->u.s.info); + lua_assert(testTMode(GET_OPCODE(*pc)) && GET_OPCODE(*pc) != OP_TESTSET && + GET_OPCODE(*pc) != OP_TEST); + SETARG_A(*pc, !(GETARG_A(*pc))); +} + + +static int jumponcond (FuncState *fs, expdesc *e, int cond) { + if (e->k == VRELOCABLE) { + Instruction ie = getcode(fs, e); + if (GET_OPCODE(ie) == OP_NOT) { + fs->pc--; /* remove previous OP_NOT */ + return condjump(fs, OP_TEST, GETARG_B(ie), 0, !cond); + } + /* else go through */ + } + discharge2anyreg(fs, e); + freeexp(fs, e); + return condjump(fs, OP_TESTSET, NO_REG, e->u.s.info, cond); +} + + +void luaK_goiftrue (FuncState *fs, expdesc *e) { + int pc; /* pc of last jump */ + luaK_dischargevars(fs, e); + switch (e->k) { + case VK: case VKNUM: case VTRUE: { + pc = NO_JUMP; /* always true; do nothing */ + break; + } + case VJMP: { + invertjump(fs, e); + pc = e->u.s.info; + break; + } + default: { + pc = jumponcond(fs, e, 0); + break; + } + } + luaK_concat(fs, &e->f, pc); /* insert last jump in `f' list */ + luaK_patchtohere(fs, e->t); + e->t = NO_JUMP; +} + + +static void luaK_goiffalse (FuncState *fs, expdesc *e) { + int pc; /* pc of last jump */ + luaK_dischargevars(fs, e); + switch (e->k) { + case VNIL: case VFALSE: { + pc = NO_JUMP; /* always false; do nothing */ + break; + } + case VJMP: { + pc = e->u.s.info; + break; + } + default: { + pc = jumponcond(fs, e, 1); + break; + } + } + luaK_concat(fs, &e->t, pc); /* insert last jump in `t' list */ + luaK_patchtohere(fs, e->f); + e->f = NO_JUMP; +} + + +static void codenot (FuncState *fs, expdesc *e) { + luaK_dischargevars(fs, e); + switch (e->k) { + case VNIL: case VFALSE: { + e->k = VTRUE; + break; + } + case VK: case VKNUM: case VTRUE: { + e->k = VFALSE; + break; + } + case VJMP: { + invertjump(fs, e); + break; + } + case VRELOCABLE: + case VNONRELOC: { + discharge2anyreg(fs, e); + freeexp(fs, e); + e->u.s.info = luaK_codeABC(fs, OP_NOT, 0, e->u.s.info, 0); + e->k = VRELOCABLE; + break; + } + default: { + lua_assert(0); /* cannot happen */ + break; + } + } + /* interchange true and false lists */ + { int temp = e->f; e->f = e->t; e->t = temp; } + removevalues(fs, e->f); + removevalues(fs, e->t); +} + + +void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) { + t->u.s.aux = luaK_exp2RK(fs, k); + t->k = VINDEXED; +} + + +static int constfolding (OpCode op, expdesc *e1, expdesc *e2) { + lua_Number v1, v2, r; + if (!isnumeral(e1) || !isnumeral(e2)) return 0; + v1 = e1->u.nval; + v2 = e2->u.nval; + switch (op) { + case OP_ADD: r = luai_numadd(v1, v2); break; + case OP_SUB: r = luai_numsub(v1, v2); break; + case OP_MUL: r = luai_nummul(v1, v2); break; + case OP_DIV: + if (v2 == 0) return 0; /* do not attempt to divide by 0 */ + r = luai_numdiv(v1, v2); break; + case OP_MOD: + if (v2 == 0) return 0; /* do not attempt to divide by 0 */ + r = luai_nummod(v1, v2); break; + case OP_POW: r = luai_numpow(v1, v2); break; + case OP_UNM: r = luai_numunm(v1); break; + case OP_LEN: return 0; /* no constant folding for 'len' */ + default: lua_assert(0); r = 0; break; + } + if (luai_numisnan(r)) return 0; /* do not attempt to produce NaN */ + e1->u.nval = r; + return 1; +} + + +static void codearith (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2) { + if (constfolding(op, e1, e2)) + return; + else { + int o2 = (op != OP_UNM && op != OP_LEN) ? luaK_exp2RK(fs, e2) : 0; + int o1 = luaK_exp2RK(fs, e1); + if (o1 > o2) { + freeexp(fs, e1); + freeexp(fs, e2); + } + else { + freeexp(fs, e2); + freeexp(fs, e1); + } + e1->u.s.info = luaK_codeABC(fs, op, 0, o1, o2); + e1->k = VRELOCABLE; + } +} + + +static void codecomp (FuncState *fs, OpCode op, int cond, expdesc *e1, + expdesc *e2) { + int o1 = luaK_exp2RK(fs, e1); + int o2 = luaK_exp2RK(fs, e2); + freeexp(fs, e2); + freeexp(fs, e1); + if (cond == 0 && op != OP_EQ) { + int temp; /* exchange args to replace by `<' or `<=' */ + temp = o1; o1 = o2; o2 = temp; /* o1 <==> o2 */ + cond = 1; + } + e1->u.s.info = condjump(fs, op, cond, o1, o2); + e1->k = VJMP; +} + + +void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e) { + expdesc e2; + e2.t = e2.f = NO_JUMP; e2.k = VKNUM; e2.u.nval = 0; + switch (op) { + case OPR_MINUS: { + if (!isnumeral(e)) + luaK_exp2anyreg(fs, e); /* cannot operate on non-numeric constants */ + codearith(fs, OP_UNM, e, &e2); + break; + } + case OPR_NOT: codenot(fs, e); break; + case OPR_LEN: { + luaK_exp2anyreg(fs, e); /* cannot operate on constants */ + codearith(fs, OP_LEN, e, &e2); + break; + } + default: lua_assert(0); + } +} + + +void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) { + switch (op) { + case OPR_AND: { + luaK_goiftrue(fs, v); + break; + } + case OPR_OR: { + luaK_goiffalse(fs, v); + break; + } + case OPR_CONCAT: { + luaK_exp2nextreg(fs, v); /* operand must be on the `stack' */ + break; + } + case OPR_ADD: case OPR_SUB: case OPR_MUL: case OPR_DIV: + case OPR_MOD: case OPR_POW: { + if (!isnumeral(v)) luaK_exp2RK(fs, v); + break; + } + default: { + luaK_exp2RK(fs, v); + break; + } + } +} + + +void luaK_posfix (FuncState *fs, BinOpr op, expdesc *e1, expdesc *e2) { + switch (op) { + case OPR_AND: { + lua_assert(e1->t == NO_JUMP); /* list must be closed */ + luaK_dischargevars(fs, e2); + luaK_concat(fs, &e2->f, e1->f); + *e1 = *e2; + break; + } + case OPR_OR: { + lua_assert(e1->f == NO_JUMP); /* list must be closed */ + luaK_dischargevars(fs, e2); + luaK_concat(fs, &e2->t, e1->t); + *e1 = *e2; + break; + } + case OPR_CONCAT: { + luaK_exp2val(fs, e2); + if (e2->k == VRELOCABLE && GET_OPCODE(getcode(fs, e2)) == OP_CONCAT) { + lua_assert(e1->u.s.info == GETARG_B(getcode(fs, e2))-1); + freeexp(fs, e1); + SETARG_B(getcode(fs, e2), e1->u.s.info); + e1->k = VRELOCABLE; e1->u.s.info = e2->u.s.info; + } + else { + luaK_exp2nextreg(fs, e2); /* operand must be on the 'stack' */ + codearith(fs, OP_CONCAT, e1, e2); + } + break; + } + case OPR_ADD: codearith(fs, OP_ADD, e1, e2); break; + case OPR_SUB: codearith(fs, OP_SUB, e1, e2); break; + case OPR_MUL: codearith(fs, OP_MUL, e1, e2); break; + case OPR_DIV: codearith(fs, OP_DIV, e1, e2); break; + case OPR_MOD: codearith(fs, OP_MOD, e1, e2); break; + case OPR_POW: codearith(fs, OP_POW, e1, e2); break; + case OPR_EQ: codecomp(fs, OP_EQ, 1, e1, e2); break; + case OPR_NE: codecomp(fs, OP_EQ, 0, e1, e2); break; + case OPR_LT: codecomp(fs, OP_LT, 1, e1, e2); break; + case OPR_LE: codecomp(fs, OP_LE, 1, e1, e2); break; + case OPR_GT: codecomp(fs, OP_LT, 0, e1, e2); break; + case OPR_GE: codecomp(fs, OP_LE, 0, e1, e2); break; + default: lua_assert(0); + } +} + + +void luaK_fixline (FuncState *fs, int line) { + fs->f->lineinfo[fs->pc - 1] = line; +} + + +static int luaK_code (FuncState *fs, Instruction i, int line) { + Proto *f = fs->f; + dischargejpc(fs); /* `pc' will change */ + /* put new instruction in code array */ + luaM_growvector(fs->L, f->code, fs->pc, f->sizecode, Instruction, + MAX_INT, "code size overflow"); + f->code[fs->pc] = i; + /* save corresponding line information */ + luaM_growvector(fs->L, f->lineinfo, fs->pc, f->sizelineinfo, int, + MAX_INT, "code size overflow"); + f->lineinfo[fs->pc] = line; + return fs->pc++; +} + + +int luaK_codeABC (FuncState *fs, OpCode o, int a, int b, int c) { + lua_assert(getOpMode(o) == iABC); + lua_assert(getBMode(o) != OpArgN || b == 0); + lua_assert(getCMode(o) != OpArgN || c == 0); + return luaK_code(fs, CREATE_ABC(o, a, b, c), fs->ls->lastline); +} + + +int luaK_codeABx (FuncState *fs, OpCode o, int a, unsigned int bc) { + lua_assert(getOpMode(o) == iABx || getOpMode(o) == iAsBx); + lua_assert(getCMode(o) == OpArgN); + return luaK_code(fs, CREATE_ABx(o, a, bc), fs->ls->lastline); +} + + +void luaK_setlist (FuncState *fs, int base, int nelems, int tostore) { + int c = (nelems - 1)/LFIELDS_PER_FLUSH + 1; + int b = (tostore == LUA_MULTRET) ? 0 : tostore; + lua_assert(tostore != 0); + if (c <= MAXARG_C) + luaK_codeABC(fs, OP_SETLIST, base, b, c); + else { + luaK_codeABC(fs, OP_SETLIST, base, b, 0); + luaK_code(fs, cast(Instruction, c), fs->ls->lastline); + } + fs->freereg = base + 1; /* free registers with list values */ +} + diff --git a/lib/lua/lcode.h b/lib/lua/lcode.h new file mode 100644 index 000000000000..b941c607212b --- /dev/null +++ b/lib/lua/lcode.h @@ -0,0 +1,76 @@ +/* +** $Id: lcode.h,v 1.48.1.1 2007/12/27 13:02:25 roberto Exp $ +** Code generator for Lua +** See Copyright Notice in lua.h +*/ + +#ifndef lcode_h +#define lcode_h + +#include "llex.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lparser.h" + + +/* +** Marks the end of a patch list. It is an invalid value both as an absolute +** address, and as a list link (would link an element to itself). +*/ +#define NO_JUMP (-1) + + +/* +** grep "ORDER OPR" if you change these enums +*/ +typedef enum BinOpr { + OPR_ADD, OPR_SUB, OPR_MUL, OPR_DIV, OPR_MOD, OPR_POW, + OPR_CONCAT, + OPR_NE, OPR_EQ, + OPR_LT, OPR_LE, OPR_GT, OPR_GE, + OPR_AND, OPR_OR, + OPR_NOBINOPR +} BinOpr; + + +typedef enum UnOpr { OPR_MINUS, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr; + + +#define getcode(fs,e) ((fs)->f->code[(e)->u.s.info]) + +#define luaK_codeAsBx(fs,o,A,sBx) luaK_codeABx(fs,o,A,(sBx)+MAXARG_sBx) + +#define luaK_setmultret(fs,e) luaK_setreturns(fs, e, LUA_MULTRET) + +LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx); +LUAI_FUNC int luaK_codeABC (FuncState *fs, OpCode o, int A, int B, int C); +LUAI_FUNC void luaK_fixline (FuncState *fs, int line); +LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n); +LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n); +LUAI_FUNC void luaK_checkstack (FuncState *fs, int n); +LUAI_FUNC int luaK_stringK (FuncState *fs, TString *s); +LUAI_FUNC int luaK_numberK (FuncState *fs, lua_Number r); +LUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e); +LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_exp2nextreg (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e); +LUAI_FUNC int luaK_exp2RK (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_self (FuncState *fs, expdesc *e, expdesc *key); +LUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k); +LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_storevar (FuncState *fs, expdesc *var, expdesc *e); +LUAI_FUNC void luaK_setreturns (FuncState *fs, expdesc *e, int nresults); +LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e); +LUAI_FUNC int luaK_jump (FuncState *fs); +LUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret); +LUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target); +LUAI_FUNC void luaK_patchtohere (FuncState *fs, int list); +LUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2); +LUAI_FUNC int luaK_getlabel (FuncState *fs); +LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v); +LUAI_FUNC void luaK_infix (FuncState *fs, BinOpr op, expdesc *v); +LUAI_FUNC void luaK_posfix (FuncState *fs, BinOpr op, expdesc *v1, expdesc *v2); +LUAI_FUNC void luaK_setlist (FuncState *fs, int base, int nelems, int tostore); + + +#endif diff --git a/lib/lua/ldblib.c b/lib/lua/ldblib.c new file mode 100644 index 000000000000..2027eda59837 --- /dev/null +++ b/lib/lua/ldblib.c @@ -0,0 +1,398 @@ +/* +** $Id: ldblib.c,v 1.104.1.4 2009/08/04 18:50:18 roberto Exp $ +** Interface from Lua to its debug API +** See Copyright Notice in lua.h +*/ + + +#include +#include +#include + +#define ldblib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + + +static int db_getregistry (lua_State *L) { + lua_pushvalue(L, LUA_REGISTRYINDEX); + return 1; +} + + +static int db_getmetatable (lua_State *L) { + luaL_checkany(L, 1); + if (!lua_getmetatable(L, 1)) { + lua_pushnil(L); /* no metatable */ + } + return 1; +} + + +static int db_setmetatable (lua_State *L) { + int t = lua_type(L, 2); + luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2, + "nil or table expected"); + lua_settop(L, 2); + lua_pushboolean(L, lua_setmetatable(L, 1)); + return 1; +} + + +static int db_getfenv (lua_State *L) { + luaL_checkany(L, 1); + lua_getfenv(L, 1); + return 1; +} + + +static int db_setfenv (lua_State *L) { + luaL_checktype(L, 2, LUA_TTABLE); + lua_settop(L, 2); + if (lua_setfenv(L, 1) == 0) + luaL_error(L, LUA_QL("setfenv") + " cannot change environment of given object"); + return 1; +} + + +static void settabss (lua_State *L, const char *i, const char *v) { + lua_pushstring(L, v); + lua_setfield(L, -2, i); +} + + +static void settabsi (lua_State *L, const char *i, int v) { + lua_pushinteger(L, v); + lua_setfield(L, -2, i); +} + + +static lua_State *getthread (lua_State *L, int *arg) { + if (lua_isthread(L, 1)) { + *arg = 1; + return lua_tothread(L, 1); + } + else { + *arg = 0; + return L; + } +} + + +static void treatstackoption (lua_State *L, lua_State *L1, const char *fname) { + if (L == L1) { + lua_pushvalue(L, -2); + lua_remove(L, -3); + } + else + lua_xmove(L1, L, 1); + lua_setfield(L, -2, fname); +} + + +static int db_getinfo (lua_State *L) { + lua_Debug ar; + int arg; + lua_State *L1 = getthread(L, &arg); + const char *options = luaL_optstring(L, arg+2, "flnSu"); + if (lua_isnumber(L, arg+1)) { + if (!lua_getstack(L1, (int)lua_tointeger(L, arg+1), &ar)) { + lua_pushnil(L); /* level out of range */ + return 1; + } + } + else if (lua_isfunction(L, arg+1)) { + lua_pushfstring(L, ">%s", options); + options = lua_tostring(L, -1); + lua_pushvalue(L, arg+1); + lua_xmove(L, L1, 1); + } + else + return luaL_argerror(L, arg+1, "function or level expected"); + if (!lua_getinfo(L1, options, &ar)) + return luaL_argerror(L, arg+2, "invalid option"); + lua_createtable(L, 0, 2); + if (strchr(options, 'S')) { + settabss(L, "source", ar.source); + settabss(L, "short_src", ar.short_src); + settabsi(L, "linedefined", ar.linedefined); + settabsi(L, "lastlinedefined", ar.lastlinedefined); + settabss(L, "what", ar.what); + } + if (strchr(options, 'l')) + settabsi(L, "currentline", ar.currentline); + if (strchr(options, 'u')) + settabsi(L, "nups", ar.nups); + if (strchr(options, 'n')) { + settabss(L, "name", ar.name); + settabss(L, "namewhat", ar.namewhat); + } + if (strchr(options, 'L')) + treatstackoption(L, L1, "activelines"); + if (strchr(options, 'f')) + treatstackoption(L, L1, "func"); + return 1; /* return table */ +} + + +static int db_getlocal (lua_State *L) { + int arg; + lua_State *L1 = getthread(L, &arg); + lua_Debug ar; + const char *name; + if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar)) /* out of range? */ + return luaL_argerror(L, arg+1, "level out of range"); + name = lua_getlocal(L1, &ar, luaL_checkint(L, arg+2)); + if (name) { + lua_xmove(L1, L, 1); + lua_pushstring(L, name); + lua_pushvalue(L, -2); + return 2; + } + else { + lua_pushnil(L); + return 1; + } +} + + +static int db_setlocal (lua_State *L) { + int arg; + lua_State *L1 = getthread(L, &arg); + lua_Debug ar; + if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar)) /* out of range? */ + return luaL_argerror(L, arg+1, "level out of range"); + luaL_checkany(L, arg+3); + lua_settop(L, arg+3); + lua_xmove(L, L1, 1); + lua_pushstring(L, lua_setlocal(L1, &ar, luaL_checkint(L, arg+2))); + return 1; +} + + +static int auxupvalue (lua_State *L, int get) { + const char *name; + int n = luaL_checkint(L, 2); + luaL_checktype(L, 1, LUA_TFUNCTION); + if (lua_iscfunction(L, 1)) return 0; /* cannot touch C upvalues from Lua */ + name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n); + if (name == NULL) return 0; + lua_pushstring(L, name); + lua_insert(L, -(get+1)); + return get + 1; +} + + +static int db_getupvalue (lua_State *L) { + return auxupvalue(L, 1); +} + + +static int db_setupvalue (lua_State *L) { + luaL_checkany(L, 3); + return auxupvalue(L, 0); +} + + + +static const char KEY_HOOK = 'h'; + + +static void hookf (lua_State *L, lua_Debug *ar) { + static const char *const hooknames[] = + {"call", "return", "line", "count", "tail return"}; + lua_pushlightuserdata(L, (void *)&KEY_HOOK); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_pushlightuserdata(L, L); + lua_rawget(L, -2); + if (lua_isfunction(L, -1)) { + lua_pushstring(L, hooknames[(int)ar->event]); + if (ar->currentline >= 0) + lua_pushinteger(L, ar->currentline); + else lua_pushnil(L); + lua_assert(lua_getinfo(L, "lS", ar)); + lua_call(L, 2, 0); + } +} + + +static int makemask (const char *smask, int count) { + int mask = 0; + if (strchr(smask, 'c')) mask |= LUA_MASKCALL; + if (strchr(smask, 'r')) mask |= LUA_MASKRET; + if (strchr(smask, 'l')) mask |= LUA_MASKLINE; + if (count > 0) mask |= LUA_MASKCOUNT; + return mask; +} + + +static char *unmakemask (int mask, char *smask) { + int i = 0; + if (mask & LUA_MASKCALL) smask[i++] = 'c'; + if (mask & LUA_MASKRET) smask[i++] = 'r'; + if (mask & LUA_MASKLINE) smask[i++] = 'l'; + smask[i] = '\0'; + return smask; +} + + +static void gethooktable (lua_State *L) { + lua_pushlightuserdata(L, (void *)&KEY_HOOK); + lua_rawget(L, LUA_REGISTRYINDEX); + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + lua_createtable(L, 0, 1); + lua_pushlightuserdata(L, (void *)&KEY_HOOK); + lua_pushvalue(L, -2); + lua_rawset(L, LUA_REGISTRYINDEX); + } +} + + +static int db_sethook (lua_State *L) { + int arg, mask, count; + lua_Hook func; + lua_State *L1 = getthread(L, &arg); + if (lua_isnoneornil(L, arg+1)) { + lua_settop(L, arg+1); + func = NULL; mask = 0; count = 0; /* turn off hooks */ + } + else { + const char *smask = luaL_checkstring(L, arg+2); + luaL_checktype(L, arg+1, LUA_TFUNCTION); + count = luaL_optint(L, arg+3, 0); + func = hookf; mask = makemask(smask, count); + } + gethooktable(L); + lua_pushlightuserdata(L, L1); + lua_pushvalue(L, arg+1); + lua_rawset(L, -3); /* set new hook */ + lua_pop(L, 1); /* remove hook table */ + lua_sethook(L1, func, mask, count); /* set hooks */ + return 0; +} + + +static int db_gethook (lua_State *L) { + int arg; + lua_State *L1 = getthread(L, &arg); + char buff[5]; + int mask = lua_gethookmask(L1); + lua_Hook hook = lua_gethook(L1); + if (hook != NULL && hook != hookf) /* external hook? */ + lua_pushliteral(L, "external hook"); + else { + gethooktable(L); + lua_pushlightuserdata(L, L1); + lua_rawget(L, -2); /* get hook */ + lua_remove(L, -2); /* remove hook table */ + } + lua_pushstring(L, unmakemask(mask, buff)); + lua_pushinteger(L, lua_gethookcount(L1)); + return 3; +} + + +static int db_debug (lua_State *L) { + for (;;) { + char buffer[250]; + fputs("lua_debug> ", stderr); + if (fgets(buffer, sizeof(buffer), stdin) == 0 || + strcmp(buffer, "cont\n") == 0) + return 0; + if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") || + lua_pcall(L, 0, 0, 0)) { + fputs(lua_tostring(L, -1), stderr); + fputs("\n", stderr); + } + lua_settop(L, 0); /* remove eventual returns */ + } +} + + +#define LEVELS1 12 /* size of the first part of the stack */ +#define LEVELS2 10 /* size of the second part of the stack */ + +static int db_errorfb (lua_State *L) { + int level; + int firstpart = 1; /* still before eventual `...' */ + int arg; + lua_State *L1 = getthread(L, &arg); + lua_Debug ar; + if (lua_isnumber(L, arg+2)) { + level = (int)lua_tointeger(L, arg+2); + lua_pop(L, 1); + } + else + level = (L == L1) ? 1 : 0; /* level 0 may be this own function */ + if (lua_gettop(L) == arg) + lua_pushliteral(L, ""); + else if (!lua_isstring(L, arg+1)) return 1; /* message is not a string */ + else lua_pushliteral(L, "\n"); + lua_pushliteral(L, "stack traceback:"); + while (lua_getstack(L1, level++, &ar)) { + if (level > LEVELS1 && firstpart) { + /* no more than `LEVELS2' more levels? */ + if (!lua_getstack(L1, level+LEVELS2, &ar)) + level--; /* keep going */ + else { + lua_pushliteral(L, "\n\t..."); /* too many levels */ + while (lua_getstack(L1, level+LEVELS2, &ar)) /* find last levels */ + level++; + } + firstpart = 0; + continue; + } + lua_pushliteral(L, "\n\t"); + lua_getinfo(L1, "Snl", &ar); + lua_pushfstring(L, "%s:", ar.short_src); + if (ar.currentline > 0) + lua_pushfstring(L, "%d:", ar.currentline); + if (*ar.namewhat != '\0') /* is there a name? */ + lua_pushfstring(L, " in function " LUA_QS, ar.name); + else { + if (*ar.what == 'm') /* main? */ + lua_pushfstring(L, " in main chunk"); + else if (*ar.what == 'C' || *ar.what == 't') + lua_pushliteral(L, " ?"); /* C function or tail call */ + else + lua_pushfstring(L, " in function <%s:%d>", + ar.short_src, ar.linedefined); + } + lua_concat(L, lua_gettop(L) - arg); + } + lua_concat(L, lua_gettop(L) - arg); + return 1; +} + + +static const luaL_Reg dblib[] = { + {"debug", db_debug}, + {"getfenv", db_getfenv}, + {"gethook", db_gethook}, + {"getinfo", db_getinfo}, + {"getlocal", db_getlocal}, + {"getregistry", db_getregistry}, + {"getmetatable", db_getmetatable}, + {"getupvalue", db_getupvalue}, + {"setfenv", db_setfenv}, + {"sethook", db_sethook}, + {"setlocal", db_setlocal}, + {"setmetatable", db_setmetatable}, + {"setupvalue", db_setupvalue}, + {"traceback", db_errorfb}, + {NULL, NULL} +}; + + +LUALIB_API int luaopen_debug (lua_State *L) { + luaL_register(L, LUA_DBLIBNAME, dblib); + return 1; +} + diff --git a/lib/lua/ldebug.c b/lib/lua/ldebug.c new file mode 100644 index 000000000000..50ad3d38035e --- /dev/null +++ b/lib/lua/ldebug.c @@ -0,0 +1,638 @@ +/* +** $Id: ldebug.c,v 2.29.1.6 2008/05/08 16:56:26 roberto Exp $ +** Debug Interface +** See Copyright Notice in lua.h +*/ + + +#include +#include +#include + + +#define ldebug_c +#define LUA_CORE + +#include "lua.h" + +#include "lapi.h" +#include "lcode.h" +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" +#include "lvm.h" + + + +static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name); + + +static int currentpc (lua_State *L, CallInfo *ci) { + if (!isLua(ci)) return -1; /* function is not a Lua function? */ + if (ci == L->ci) + ci->savedpc = L->savedpc; + return pcRel(ci->savedpc, ci_func(ci)->l.p); +} + + +static int currentline (lua_State *L, CallInfo *ci) { + int pc = currentpc(L, ci); + if (pc < 0) + return -1; /* only active lua functions have current-line information */ + else + return getline(ci_func(ci)->l.p, pc); +} + + +/* +** this function can be called asynchronous (e.g. during a signal) +*/ +LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count) { + if (func == NULL || mask == 0) { /* turn off hooks? */ + mask = 0; + func = NULL; + } + L->hook = func; + L->basehookcount = count; + resethookcount(L); + L->hookmask = cast_byte(mask); + return 1; +} + + +LUA_API lua_Hook lua_gethook (lua_State *L) { + return L->hook; +} + + +LUA_API int lua_gethookmask (lua_State *L) { + return L->hookmask; +} + + +LUA_API int lua_gethookcount (lua_State *L) { + return L->basehookcount; +} + + +LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar) { + int status; + CallInfo *ci; + lua_lock(L); + for (ci = L->ci; level > 0 && ci > L->base_ci; ci--) { + level--; + if (f_isLua(ci)) /* Lua function? */ + level -= ci->tailcalls; /* skip lost tail calls */ + } + if (level == 0 && ci > L->base_ci) { /* level found? */ + status = 1; + ar->i_ci = cast_int(ci - L->base_ci); + } + else if (level < 0) { /* level is of a lost tail call? */ + status = 1; + ar->i_ci = 0; + } + else status = 0; /* no such level */ + lua_unlock(L); + return status; +} + + +static Proto *getluaproto (CallInfo *ci) { + return (isLua(ci) ? ci_func(ci)->l.p : NULL); +} + + +static const char *findlocal (lua_State *L, CallInfo *ci, int n) { + const char *name; + Proto *fp = getluaproto(ci); + if (fp && (name = luaF_getlocalname(fp, n, currentpc(L, ci))) != NULL) + return name; /* is a local variable in a Lua function */ + else { + StkId limit = (ci == L->ci) ? L->top : (ci+1)->func; + if (limit - ci->base >= n && n > 0) /* is 'n' inside 'ci' stack? */ + return "(*temporary)"; + else + return NULL; + } +} + + +LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) { + CallInfo *ci = L->base_ci + ar->i_ci; + const char *name = findlocal(L, ci, n); + lua_lock(L); + if (name) + luaA_pushobject(L, ci->base + (n - 1)); + lua_unlock(L); + return name; +} + + +LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) { + CallInfo *ci = L->base_ci + ar->i_ci; + const char *name = findlocal(L, ci, n); + lua_lock(L); + if (name) + setobjs2s(L, ci->base + (n - 1), L->top - 1); + L->top--; /* pop value */ + lua_unlock(L); + return name; +} + + +static void funcinfo (lua_Debug *ar, Closure *cl) { + if (cl->c.isC) { + ar->source = "=[C]"; + ar->linedefined = -1; + ar->lastlinedefined = -1; + ar->what = "C"; + } + else { + ar->source = getstr(cl->l.p->source); + ar->linedefined = cl->l.p->linedefined; + ar->lastlinedefined = cl->l.p->lastlinedefined; + ar->what = (ar->linedefined == 0) ? "main" : "Lua"; + } + luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE); +} + + +static void info_tailcall (lua_Debug *ar) { + ar->name = ar->namewhat = ""; + ar->what = "tail"; + ar->lastlinedefined = ar->linedefined = ar->currentline = -1; + ar->source = "=(tail call)"; + luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE); + ar->nups = 0; +} + + +static void collectvalidlines (lua_State *L, Closure *f) { + if (f == NULL || f->c.isC) { + setnilvalue(L->top); + } + else { + Table *t = luaH_new(L, 0, 0); + int *lineinfo = f->l.p->lineinfo; + int i; + for (i=0; il.p->sizelineinfo; i++) + setbvalue(luaH_setnum(L, t, lineinfo[i]), 1); + sethvalue(L, L->top, t); + } + incr_top(L); +} + + +static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar, + Closure *f, CallInfo *ci) { + int status = 1; + if (f == NULL) { + info_tailcall(ar); + return status; + } + for (; *what; what++) { + switch (*what) { + case 'S': { + funcinfo(ar, f); + break; + } + case 'l': { + ar->currentline = (ci) ? currentline(L, ci) : -1; + break; + } + case 'u': { + ar->nups = f->c.nupvalues; + break; + } + case 'n': { + ar->namewhat = (ci) ? getfuncname(L, ci, &ar->name) : NULL; + if (ar->namewhat == NULL) { + ar->namewhat = ""; /* not found */ + ar->name = NULL; + } + break; + } + case 'L': + case 'f': /* handled by lua_getinfo */ + break; + default: status = 0; /* invalid option */ + } + } + return status; +} + + +LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) { + int status; + Closure *f = NULL; + CallInfo *ci = NULL; + lua_lock(L); + if (*what == '>') { + StkId func = L->top - 1; + luai_apicheck(L, ttisfunction(func)); + what++; /* skip the '>' */ + f = clvalue(func); + L->top--; /* pop function */ + } + else if (ar->i_ci != 0) { /* no tail call? */ + ci = L->base_ci + ar->i_ci; + lua_assert(ttisfunction(ci->func)); + f = clvalue(ci->func); + } + status = auxgetinfo(L, what, ar, f, ci); + if (strchr(what, 'f')) { + if (f == NULL) setnilvalue(L->top); + else setclvalue(L, L->top, f); + incr_top(L); + } + if (strchr(what, 'L')) + collectvalidlines(L, f); + lua_unlock(L); + return status; +} + + +/* +** {====================================================== +** Symbolic Execution and code checker +** ======================================================= +*/ + +#define check(x) if (!(x)) return 0; + +#define checkjump(pt,pc) check(0 <= pc && pc < pt->sizecode) + +#define checkreg(pt,reg) check((reg) < (pt)->maxstacksize) + + + +static int precheck (const Proto *pt) { + check(pt->maxstacksize <= MAXSTACK); + check(pt->numparams+(pt->is_vararg & VARARG_HASARG) <= pt->maxstacksize); + check(!(pt->is_vararg & VARARG_NEEDSARG) || + (pt->is_vararg & VARARG_HASARG)); + check(pt->sizeupvalues <= pt->nups); + check(pt->sizelineinfo == pt->sizecode || pt->sizelineinfo == 0); + check(pt->sizecode > 0 && GET_OPCODE(pt->code[pt->sizecode-1]) == OP_RETURN); + return 1; +} + + +#define checkopenop(pt,pc) luaG_checkopenop((pt)->code[(pc)+1]) + +int luaG_checkopenop (Instruction i) { + switch (GET_OPCODE(i)) { + case OP_CALL: + case OP_TAILCALL: + case OP_RETURN: + case OP_SETLIST: { + check(GETARG_B(i) == 0); + return 1; + } + default: return 0; /* invalid instruction after an open call */ + } +} + + +static int checkArgMode (const Proto *pt, int r, enum OpArgMask mode) { + switch (mode) { + case OpArgN: check(r == 0); break; + case OpArgU: break; + case OpArgR: checkreg(pt, r); break; + case OpArgK: + check(ISK(r) ? INDEXK(r) < pt->sizek : r < pt->maxstacksize); + break; + } + return 1; +} + + +static Instruction symbexec (const Proto *pt, int lastpc, int reg) { + int pc; + int last; /* stores position of last instruction that changed `reg' */ + last = pt->sizecode-1; /* points to final return (a `neutral' instruction) */ + check(precheck(pt)); + for (pc = 0; pc < lastpc; pc++) { + Instruction i = pt->code[pc]; + OpCode op = GET_OPCODE(i); + int a = GETARG_A(i); + int b = 0; + int c = 0; + check(op < NUM_OPCODES); + checkreg(pt, a); + switch (getOpMode(op)) { + case iABC: { + b = GETARG_B(i); + c = GETARG_C(i); + check(checkArgMode(pt, b, getBMode(op))); + check(checkArgMode(pt, c, getCMode(op))); + break; + } + case iABx: { + b = GETARG_Bx(i); + if (getBMode(op) == OpArgK) check(b < pt->sizek); + break; + } + case iAsBx: { + b = GETARG_sBx(i); + if (getBMode(op) == OpArgR) { + int dest = pc+1+b; + check(0 <= dest && dest < pt->sizecode); + if (dest > 0) { + int j; + /* check that it does not jump to a setlist count; this + is tricky, because the count from a previous setlist may + have the same value of an invalid setlist; so, we must + go all the way back to the first of them (if any) */ + for (j = 0; j < dest; j++) { + Instruction d = pt->code[dest-1-j]; + if (!(GET_OPCODE(d) == OP_SETLIST && GETARG_C(d) == 0)) break; + } + /* if 'j' is even, previous value is not a setlist (even if + it looks like one) */ + check((j&1) == 0); + } + } + break; + } + } + if (testAMode(op)) { + if (a == reg) last = pc; /* change register `a' */ + } + if (testTMode(op)) { + check(pc+2 < pt->sizecode); /* check skip */ + check(GET_OPCODE(pt->code[pc+1]) == OP_JMP); + } + switch (op) { + case OP_LOADBOOL: { + if (c == 1) { /* does it jump? */ + check(pc+2 < pt->sizecode); /* check its jump */ + check(GET_OPCODE(pt->code[pc+1]) != OP_SETLIST || + GETARG_C(pt->code[pc+1]) != 0); + } + break; + } + case OP_LOADNIL: { + if (a <= reg && reg <= b) + last = pc; /* set registers from `a' to `b' */ + break; + } + case OP_GETUPVAL: + case OP_SETUPVAL: { + check(b < pt->nups); + break; + } + case OP_GETGLOBAL: + case OP_SETGLOBAL: { + check(ttisstring(&pt->k[b])); + break; + } + case OP_SELF: { + checkreg(pt, a+1); + if (reg == a+1) last = pc; + break; + } + case OP_CONCAT: { + check(b < c); /* at least two operands */ + break; + } + case OP_TFORLOOP: { + check(c >= 1); /* at least one result (control variable) */ + checkreg(pt, a+2+c); /* space for results */ + if (reg >= a+2) last = pc; /* affect all regs above its base */ + break; + } + case OP_FORLOOP: + case OP_FORPREP: + checkreg(pt, a+3); + /* go through */ + case OP_JMP: { + int dest = pc+1+b; + /* not full check and jump is forward and do not skip `lastpc'? */ + if (reg != NO_REG && pc < dest && dest <= lastpc) + pc += b; /* do the jump */ + break; + } + case OP_CALL: + case OP_TAILCALL: { + if (b != 0) { + checkreg(pt, a+b-1); + } + c--; /* c = num. returns */ + if (c == LUA_MULTRET) { + check(checkopenop(pt, pc)); + } + else if (c != 0) + checkreg(pt, a+c-1); + if (reg >= a) last = pc; /* affect all registers above base */ + break; + } + case OP_RETURN: { + b--; /* b = num. returns */ + if (b > 0) checkreg(pt, a+b-1); + break; + } + case OP_SETLIST: { + if (b > 0) checkreg(pt, a + b); + if (c == 0) { + pc++; + check(pc < pt->sizecode - 1); + } + break; + } + case OP_CLOSURE: { + int nup, j; + check(b < pt->sizep); + nup = pt->p[b]->nups; + check(pc + nup < pt->sizecode); + for (j = 1; j <= nup; j++) { + OpCode op1 = GET_OPCODE(pt->code[pc + j]); + check(op1 == OP_GETUPVAL || op1 == OP_MOVE); + } + if (reg != NO_REG) /* tracing? */ + pc += nup; /* do not 'execute' these pseudo-instructions */ + break; + } + case OP_VARARG: { + check((pt->is_vararg & VARARG_ISVARARG) && + !(pt->is_vararg & VARARG_NEEDSARG)); + b--; + if (b == LUA_MULTRET) check(checkopenop(pt, pc)); + checkreg(pt, a+b-1); + break; + } + default: break; + } + } + return pt->code[last]; +} + +#undef check +#undef checkjump +#undef checkreg + +/* }====================================================== */ + + +int luaG_checkcode (const Proto *pt) { + return (symbexec(pt, pt->sizecode, NO_REG) != 0); +} + + +static const char *kname (Proto *p, int c) { + if (ISK(c) && ttisstring(&p->k[INDEXK(c)])) + return svalue(&p->k[INDEXK(c)]); + else + return "?"; +} + + +static const char *getobjname (lua_State *L, CallInfo *ci, int stackpos, + const char **name) { + if (isLua(ci)) { /* a Lua function? */ + Proto *p = ci_func(ci)->l.p; + int pc = currentpc(L, ci); + Instruction i; + *name = luaF_getlocalname(p, stackpos+1, pc); + if (*name) /* is a local? */ + return "local"; + i = symbexec(p, pc, stackpos); /* try symbolic execution */ + lua_assert(pc != -1); + switch (GET_OPCODE(i)) { + case OP_GETGLOBAL: { + int g = GETARG_Bx(i); /* global index */ + lua_assert(ttisstring(&p->k[g])); + *name = svalue(&p->k[g]); + return "global"; + } + case OP_MOVE: { + int a = GETARG_A(i); + int b = GETARG_B(i); /* move from `b' to `a' */ + if (b < a) + return getobjname(L, ci, b, name); /* get name for `b' */ + break; + } + case OP_GETTABLE: { + int k = GETARG_C(i); /* key index */ + *name = kname(p, k); + return "field"; + } + case OP_GETUPVAL: { + int u = GETARG_B(i); /* upvalue index */ + *name = p->upvalues ? getstr(p->upvalues[u]) : "?"; + return "upvalue"; + } + case OP_SELF: { + int k = GETARG_C(i); /* key index */ + *name = kname(p, k); + return "method"; + } + default: break; + } + } + return NULL; /* no useful name found */ +} + + +static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) { + Instruction i; + if ((isLua(ci) && ci->tailcalls > 0) || !isLua(ci - 1)) + return NULL; /* calling function is not Lua (or is unknown) */ + ci--; /* calling function */ + i = ci_func(ci)->l.p->code[currentpc(L, ci)]; + if (GET_OPCODE(i) == OP_CALL || GET_OPCODE(i) == OP_TAILCALL || + GET_OPCODE(i) == OP_TFORLOOP) + return getobjname(L, ci, GETARG_A(i), name); + else + return NULL; /* no useful name can be found */ +} + + +/* only ANSI way to check whether a pointer points to an array */ +static int isinstack (CallInfo *ci, const TValue *o) { + StkId p; + for (p = ci->base; p < ci->top; p++) + if (o == p) return 1; + return 0; +} + + +void luaG_typeerror (lua_State *L, const TValue *o, const char *op) { + const char *name = NULL; + const char *t = luaT_typenames[ttype(o)]; + const char *kind = (isinstack(L->ci, o)) ? + getobjname(L, L->ci, cast_int(o - L->base), &name) : + NULL; + if (kind) + luaG_runerror(L, "attempt to %s %s " LUA_QS " (a %s value)", + op, kind, name, t); + else + luaG_runerror(L, "attempt to %s a %s value", op, t); +} + + +void luaG_concaterror (lua_State *L, StkId p1, StkId p2) { + if (ttisstring(p1) || ttisnumber(p1)) p1 = p2; + lua_assert(!ttisstring(p1) && !ttisnumber(p1)); + luaG_typeerror(L, p1, "concatenate"); +} + + +void luaG_aritherror (lua_State *L, const TValue *p1, const TValue *p2) { + TValue temp; + if (luaV_tonumber(p1, &temp) == NULL) + p2 = p1; /* first operand is wrong */ + luaG_typeerror(L, p2, "perform arithmetic on"); +} + + +int luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) { + const char *t1 = luaT_typenames[ttype(p1)]; + const char *t2 = luaT_typenames[ttype(p2)]; + if (t1[2] == t2[2]) + luaG_runerror(L, "attempt to compare two %s values", t1); + else + luaG_runerror(L, "attempt to compare %s with %s", t1, t2); + return 0; +} + + +static void addinfo (lua_State *L, const char *msg) { + CallInfo *ci = L->ci; + if (isLua(ci)) { /* is Lua code? */ + char buff[LUA_IDSIZE]; /* add file:line information */ + int line = currentline(L, ci); + luaO_chunkid(buff, getstr(getluaproto(ci)->source), LUA_IDSIZE); + luaO_pushfstring(L, "%s:%d: %s", buff, line, msg); + } +} + + +void luaG_errormsg (lua_State *L) { + if (L->errfunc != 0) { /* is there an error handling function? */ + StkId errfunc = restorestack(L, L->errfunc); + if (!ttisfunction(errfunc)) luaD_throw(L, LUA_ERRERR); + setobjs2s(L, L->top, L->top - 1); /* move argument */ + setobjs2s(L, L->top - 1, errfunc); /* push function */ + incr_top(L); + luaD_call(L, L->top - 2, 1); /* call it */ + } + luaD_throw(L, LUA_ERRRUN); +} + + +void luaG_runerror (lua_State *L, const char *fmt, ...) { + va_list argp; + va_start(argp, fmt); + addinfo(L, luaO_pushvfstring(L, fmt, argp)); + va_end(argp); + luaG_errormsg(L); +} + diff --git a/lib/lua/ldebug.h b/lib/lua/ldebug.h new file mode 100644 index 000000000000..ba28a97248eb --- /dev/null +++ b/lib/lua/ldebug.h @@ -0,0 +1,33 @@ +/* +** $Id: ldebug.h,v 2.3.1.1 2007/12/27 13:02:25 roberto Exp $ +** Auxiliary functions from Debug Interface module +** See Copyright Notice in lua.h +*/ + +#ifndef ldebug_h +#define ldebug_h + + +#include "lstate.h" + + +#define pcRel(pc, p) (cast(int, (pc) - (p)->code) - 1) + +#define getline(f,pc) (((f)->lineinfo) ? (f)->lineinfo[pc] : 0) + +#define resethookcount(L) (L->hookcount = L->basehookcount) + + +LUAI_FUNC void luaG_typeerror (lua_State *L, const TValue *o, + const char *opname); +LUAI_FUNC void luaG_concaterror (lua_State *L, StkId p1, StkId p2); +LUAI_FUNC void luaG_aritherror (lua_State *L, const TValue *p1, + const TValue *p2); +LUAI_FUNC int luaG_ordererror (lua_State *L, const TValue *p1, + const TValue *p2); +LUAI_FUNC void luaG_runerror (lua_State *L, const char *fmt, ...); +LUAI_FUNC void luaG_errormsg (lua_State *L); +LUAI_FUNC int luaG_checkcode (const Proto *pt); +LUAI_FUNC int luaG_checkopenop (Instruction i); + +#endif diff --git a/lib/lua/ldo.c b/lib/lua/ldo.c new file mode 100644 index 000000000000..d1bf786cb720 --- /dev/null +++ b/lib/lua/ldo.c @@ -0,0 +1,519 @@ +/* +** $Id: ldo.c,v 2.38.1.4 2012/01/18 02:27:10 roberto Exp $ +** Stack and Call structure of Lua +** See Copyright Notice in lua.h +*/ + + +#include +#include +#include + +#define ldo_c +#define LUA_CORE + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lparser.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" +#include "lundump.h" +#include "lvm.h" +#include "lzio.h" + + + + +/* +** {====================================================== +** Error-recovery functions +** ======================================================= +*/ + + +/* chain list of long jump buffers */ +struct lua_longjmp { + struct lua_longjmp *previous; + luai_jmpbuf b; + volatile int status; /* error code */ +}; + + +void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) { + switch (errcode) { + case LUA_ERRMEM: { + setsvalue2s(L, oldtop, luaS_newliteral(L, MEMERRMSG)); + break; + } + case LUA_ERRERR: { + setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling")); + break; + } + case LUA_ERRSYNTAX: + case LUA_ERRRUN: { + setobjs2s(L, oldtop, L->top - 1); /* error message on current top */ + break; + } + } + L->top = oldtop + 1; +} + + +static void restore_stack_limit (lua_State *L) { + lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1); + if (L->size_ci > LUAI_MAXCALLS) { /* there was an overflow? */ + int inuse = cast_int(L->ci - L->base_ci); + if (inuse + 1 < LUAI_MAXCALLS) /* can `undo' overflow? */ + luaD_reallocCI(L, LUAI_MAXCALLS); + } +} + + +static void resetstack (lua_State *L, int status) { + L->ci = L->base_ci; + L->base = L->ci->base; + luaF_close(L, L->base); /* close eventual pending closures */ + luaD_seterrorobj(L, status, L->base); + L->nCcalls = L->baseCcalls; + L->allowhook = 1; + restore_stack_limit(L); + L->errfunc = 0; + L->errorJmp = NULL; +} + + +void luaD_throw (lua_State *L, int errcode) { + if (L->errorJmp) { + L->errorJmp->status = errcode; + LUAI_THROW(L, L->errorJmp); + } + else { + L->status = cast_byte(errcode); + if (G(L)->panic) { + resetstack(L, errcode); + lua_unlock(L); + G(L)->panic(L); + } + exit(EXIT_FAILURE); + } +} + + +int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { + struct lua_longjmp lj; + lj.status = 0; + lj.previous = L->errorJmp; /* chain new error handler */ + L->errorJmp = &lj; + LUAI_TRY(L, &lj, + (*f)(L, ud); + ); + L->errorJmp = lj.previous; /* restore old error handler */ + return lj.status; +} + +/* }====================================================== */ + + +static void correctstack (lua_State *L, TValue *oldstack) { + CallInfo *ci; + GCObject *up; + L->top = (L->top - oldstack) + L->stack; + for (up = L->openupval; up != NULL; up = up->gch.next) + gco2uv(up)->v = (gco2uv(up)->v - oldstack) + L->stack; + for (ci = L->base_ci; ci <= L->ci; ci++) { + ci->top = (ci->top - oldstack) + L->stack; + ci->base = (ci->base - oldstack) + L->stack; + ci->func = (ci->func - oldstack) + L->stack; + } + L->base = (L->base - oldstack) + L->stack; +} + + +void luaD_reallocstack (lua_State *L, int newsize) { + TValue *oldstack = L->stack; + int realsize = newsize + 1 + EXTRA_STACK; + lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1); + luaM_reallocvector(L, L->stack, L->stacksize, realsize, TValue); + L->stacksize = realsize; + L->stack_last = L->stack+newsize; + correctstack(L, oldstack); +} + + +void luaD_reallocCI (lua_State *L, int newsize) { + CallInfo *oldci = L->base_ci; + luaM_reallocvector(L, L->base_ci, L->size_ci, newsize, CallInfo); + L->size_ci = newsize; + L->ci = (L->ci - oldci) + L->base_ci; + L->end_ci = L->base_ci + L->size_ci - 1; +} + + +void luaD_growstack (lua_State *L, int n) { + if (n <= L->stacksize) /* double size is enough? */ + luaD_reallocstack(L, 2*L->stacksize); + else + luaD_reallocstack(L, L->stacksize + n); +} + + +static CallInfo *growCI (lua_State *L) { + if (L->size_ci > LUAI_MAXCALLS) /* overflow while handling overflow? */ + luaD_throw(L, LUA_ERRERR); + else { + luaD_reallocCI(L, 2*L->size_ci); + if (L->size_ci > LUAI_MAXCALLS) + luaG_runerror(L, "stack overflow"); + } + return ++L->ci; +} + + +void luaD_callhook (lua_State *L, int event, int line) { + lua_Hook hook = L->hook; + if (hook && L->allowhook) { + ptrdiff_t top = savestack(L, L->top); + ptrdiff_t ci_top = savestack(L, L->ci->top); + lua_Debug ar; + ar.event = event; + ar.currentline = line; + if (event == LUA_HOOKTAILRET) + ar.i_ci = 0; /* tail call; no debug information about it */ + else + ar.i_ci = cast_int(L->ci - L->base_ci); + luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */ + L->ci->top = L->top + LUA_MINSTACK; + lua_assert(L->ci->top <= L->stack_last); + L->allowhook = 0; /* cannot call hooks inside a hook */ + lua_unlock(L); + (*hook)(L, &ar); + lua_lock(L); + lua_assert(!L->allowhook); + L->allowhook = 1; + L->ci->top = restorestack(L, ci_top); + L->top = restorestack(L, top); + } +} + + +static StkId adjust_varargs (lua_State *L, Proto *p, int actual) { + int i; + int nfixargs = p->numparams; + Table *htab = NULL; + StkId base, fixed; + for (; actual < nfixargs; ++actual) + setnilvalue(L->top++); +#if defined(LUA_COMPAT_VARARG) + if (p->is_vararg & VARARG_NEEDSARG) { /* compat. with old-style vararg? */ + int nvar = actual - nfixargs; /* number of extra arguments */ + lua_assert(p->is_vararg & VARARG_HASARG); + luaC_checkGC(L); + luaD_checkstack(L, p->maxstacksize); + htab = luaH_new(L, nvar, 1); /* create `arg' table */ + for (i=0; itop - nvar + i); + /* store counter in field `n' */ + setnvalue(luaH_setstr(L, htab, luaS_newliteral(L, "n")), cast_num(nvar)); + } +#endif + /* move fixed parameters to final position */ + fixed = L->top - actual; /* first fixed argument */ + base = L->top; /* final position of first argument */ + for (i=0; itop++, fixed+i); + setnilvalue(fixed+i); + } + /* add `arg' parameter */ + if (htab) { + sethvalue(L, L->top++, htab); + lua_assert(iswhite(obj2gco(htab))); + } + return base; +} + + +static StkId tryfuncTM (lua_State *L, StkId func) { + const TValue *tm = luaT_gettmbyobj(L, func, TM_CALL); + StkId p; + ptrdiff_t funcr = savestack(L, func); + if (!ttisfunction(tm)) + luaG_typeerror(L, func, "call"); + /* Open a hole inside the stack at `func' */ + for (p = L->top; p > func; p--) setobjs2s(L, p, p-1); + incr_top(L); + func = restorestack(L, funcr); /* previous call may change stack */ + setobj2s(L, func, tm); /* tag method is the new function to be called */ + return func; +} + + + +#define inc_ci(L) \ + ((L->ci == L->end_ci) ? growCI(L) : \ + (condhardstacktests(luaD_reallocCI(L, L->size_ci)), ++L->ci)) + + +int luaD_precall (lua_State *L, StkId func, int nresults) { + LClosure *cl; + ptrdiff_t funcr; + if (!ttisfunction(func)) /* `func' is not a function? */ + func = tryfuncTM(L, func); /* check the `function' tag method */ + funcr = savestack(L, func); + cl = &clvalue(func)->l; + L->ci->savedpc = L->savedpc; + if (!cl->isC) { /* Lua function? prepare its call */ + CallInfo *ci; + StkId st, base; + Proto *p = cl->p; + luaD_checkstack(L, p->maxstacksize); + func = restorestack(L, funcr); + if (!p->is_vararg) { /* no varargs? */ + base = func + 1; + if (L->top > base + p->numparams) + L->top = base + p->numparams; + } + else { /* vararg function */ + int nargs = cast_int(L->top - func) - 1; + base = adjust_varargs(L, p, nargs); + func = restorestack(L, funcr); /* previous call may change the stack */ + } + ci = inc_ci(L); /* now `enter' new function */ + ci->func = func; + L->base = ci->base = base; + ci->top = L->base + p->maxstacksize; + lua_assert(ci->top <= L->stack_last); + L->savedpc = p->code; /* starting point */ + ci->tailcalls = 0; + ci->nresults = nresults; + for (st = L->top; st < ci->top; st++) + setnilvalue(st); + L->top = ci->top; + if (L->hookmask & LUA_MASKCALL) { + L->savedpc++; /* hooks assume 'pc' is already incremented */ + luaD_callhook(L, LUA_HOOKCALL, -1); + L->savedpc--; /* correct 'pc' */ + } + return PCRLUA; + } + else { /* if is a C function, call it */ + CallInfo *ci; + int n; + luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */ + ci = inc_ci(L); /* now `enter' new function */ + ci->func = restorestack(L, funcr); + L->base = ci->base = ci->func + 1; + ci->top = L->top + LUA_MINSTACK; + lua_assert(ci->top <= L->stack_last); + ci->nresults = nresults; + if (L->hookmask & LUA_MASKCALL) + luaD_callhook(L, LUA_HOOKCALL, -1); + lua_unlock(L); + n = (*curr_func(L)->c.f)(L); /* do the actual call */ + lua_lock(L); + if (n < 0) /* yielding? */ + return PCRYIELD; + else { + luaD_poscall(L, L->top - n); + return PCRC; + } + } +} + + +static StkId callrethooks (lua_State *L, StkId firstResult) { + ptrdiff_t fr = savestack(L, firstResult); /* next call may change stack */ + luaD_callhook(L, LUA_HOOKRET, -1); + if (f_isLua(L->ci)) { /* Lua function? */ + while ((L->hookmask & LUA_MASKRET) && L->ci->tailcalls--) /* tail calls */ + luaD_callhook(L, LUA_HOOKTAILRET, -1); + } + return restorestack(L, fr); +} + + +int luaD_poscall (lua_State *L, StkId firstResult) { + StkId res; + int wanted, i; + CallInfo *ci; + if (L->hookmask & LUA_MASKRET) + firstResult = callrethooks(L, firstResult); + ci = L->ci--; + res = ci->func; /* res == final position of 1st result */ + wanted = ci->nresults; + L->base = (ci - 1)->base; /* restore base */ + L->savedpc = (ci - 1)->savedpc; /* restore savedpc */ + /* move results to correct place */ + for (i = wanted; i != 0 && firstResult < L->top; i--) + setobjs2s(L, res++, firstResult++); + while (i-- > 0) + setnilvalue(res++); + L->top = res; + return (wanted - LUA_MULTRET); /* 0 iff wanted == LUA_MULTRET */ +} + + +/* +** Call a function (C or Lua). The function to be called is at *func. +** The arguments are on the stack, right after the function. +** When returns, all the results are on the stack, starting at the original +** function position. +*/ +void luaD_call (lua_State *L, StkId func, int nResults) { + if (++L->nCcalls >= LUAI_MAXCCALLS) { + if (L->nCcalls == LUAI_MAXCCALLS) + luaG_runerror(L, "C stack overflow"); + else if (L->nCcalls >= (LUAI_MAXCCALLS + (LUAI_MAXCCALLS>>3))) + luaD_throw(L, LUA_ERRERR); /* error while handing stack error */ + } + if (luaD_precall(L, func, nResults) == PCRLUA) /* is a Lua function? */ + luaV_execute(L, 1); /* call it */ + L->nCcalls--; + luaC_checkGC(L); +} + + +static void resume (lua_State *L, void *ud) { + StkId firstArg = cast(StkId, ud); + CallInfo *ci = L->ci; + if (L->status == 0) { /* start coroutine? */ + lua_assert(ci == L->base_ci && firstArg > L->base); + if (luaD_precall(L, firstArg - 1, LUA_MULTRET) != PCRLUA) + return; + } + else { /* resuming from previous yield */ + lua_assert(L->status == LUA_YIELD); + L->status = 0; + if (!f_isLua(ci)) { /* `common' yield? */ + /* finish interrupted execution of `OP_CALL' */ + lua_assert(GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_CALL || + GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_TAILCALL); + if (luaD_poscall(L, firstArg)) /* complete it... */ + L->top = L->ci->top; /* and correct top if not multiple results */ + } + else /* yielded inside a hook: just continue its execution */ + L->base = L->ci->base; + } + luaV_execute(L, cast_int(L->ci - L->base_ci)); +} + + +static int resume_error (lua_State *L, const char *msg) { + L->top = L->ci->base; + setsvalue2s(L, L->top, luaS_new(L, msg)); + incr_top(L); + lua_unlock(L); + return LUA_ERRRUN; +} + + +LUA_API int lua_resume (lua_State *L, int nargs) { + int status; + lua_lock(L); + if (L->status != LUA_YIELD && (L->status != 0 || L->ci != L->base_ci)) + return resume_error(L, "cannot resume non-suspended coroutine"); + if (L->nCcalls >= LUAI_MAXCCALLS) + return resume_error(L, "C stack overflow"); + luai_userstateresume(L, nargs); + lua_assert(L->errfunc == 0); + L->baseCcalls = ++L->nCcalls; + status = luaD_rawrunprotected(L, resume, L->top - nargs); + if (status != 0) { /* error? */ + L->status = cast_byte(status); /* mark thread as `dead' */ + luaD_seterrorobj(L, status, L->top); + L->ci->top = L->top; + } + else { + lua_assert(L->nCcalls == L->baseCcalls); + status = L->status; + } + --L->nCcalls; + lua_unlock(L); + return status; +} + + +LUA_API int lua_yield (lua_State *L, int nresults) { + luai_userstateyield(L, nresults); + lua_lock(L); + if (L->nCcalls > L->baseCcalls) + luaG_runerror(L, "attempt to yield across metamethod/C-call boundary"); + L->base = L->top - nresults; /* protect stack slots below */ + L->status = LUA_YIELD; + lua_unlock(L); + return -1; +} + + +int luaD_pcall (lua_State *L, Pfunc func, void *u, + ptrdiff_t old_top, ptrdiff_t ef) { + int status; + unsigned short oldnCcalls = L->nCcalls; + ptrdiff_t old_ci = saveci(L, L->ci); + lu_byte old_allowhooks = L->allowhook; + ptrdiff_t old_errfunc = L->errfunc; + L->errfunc = ef; + status = luaD_rawrunprotected(L, func, u); + if (status != 0) { /* an error occurred? */ + StkId oldtop = restorestack(L, old_top); + luaF_close(L, oldtop); /* close eventual pending closures */ + luaD_seterrorobj(L, status, oldtop); + L->nCcalls = oldnCcalls; + L->ci = restoreci(L, old_ci); + L->base = L->ci->base; + L->savedpc = L->ci->savedpc; + L->allowhook = old_allowhooks; + restore_stack_limit(L); + } + L->errfunc = old_errfunc; + return status; +} + + + +/* +** Execute a protected parser. +*/ +struct SParser { /* data to `f_parser' */ + ZIO *z; + Mbuffer buff; /* buffer to be used by the scanner */ + const char *name; +}; + +static void f_parser (lua_State *L, void *ud) { + int i; + Proto *tf; + Closure *cl; + struct SParser *p = cast(struct SParser *, ud); + int c = luaZ_lookahead(p->z); + luaC_checkGC(L); + tf = ((c == LUA_SIGNATURE[0]) ? luaU_undump : luaY_parser)(L, p->z, + &p->buff, p->name); + cl = luaF_newLclosure(L, tf->nups, hvalue(gt(L))); + cl->l.p = tf; + for (i = 0; i < tf->nups; i++) /* initialize eventual upvalues */ + cl->l.upvals[i] = luaF_newupval(L); + setclvalue(L, L->top, cl); + incr_top(L); +} + + +int luaD_protectedparser (lua_State *L, ZIO *z, const char *name) { + struct SParser p; + int status; + p.z = z; p.name = name; + luaZ_initbuffer(L, &p.buff); + status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc); + luaZ_freebuffer(L, &p.buff); + return status; +} + + diff --git a/lib/lua/ldo.h b/lib/lua/ldo.h new file mode 100644 index 000000000000..98fddac59fbd --- /dev/null +++ b/lib/lua/ldo.h @@ -0,0 +1,57 @@ +/* +** $Id: ldo.h,v 2.7.1.1 2007/12/27 13:02:25 roberto Exp $ +** Stack and Call structure of Lua +** See Copyright Notice in lua.h +*/ + +#ifndef ldo_h +#define ldo_h + + +#include "lobject.h" +#include "lstate.h" +#include "lzio.h" + + +#define luaD_checkstack(L,n) \ + if ((char *)L->stack_last - (char *)L->top <= (n)*(int)sizeof(TValue)) \ + luaD_growstack(L, n); \ + else condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1)); + + +#define incr_top(L) {luaD_checkstack(L,1); L->top++;} + +#define savestack(L,p) ((char *)(p) - (char *)L->stack) +#define restorestack(L,n) ((TValue *)((char *)L->stack + (n))) + +#define saveci(L,p) ((char *)(p) - (char *)L->base_ci) +#define restoreci(L,n) ((CallInfo *)((char *)L->base_ci + (n))) + + +/* results from luaD_precall */ +#define PCRLUA 0 /* initiated a call to a Lua function */ +#define PCRC 1 /* did a call to a C function */ +#define PCRYIELD 2 /* C funtion yielded */ + + +/* type of protected functions, to be ran by `runprotected' */ +typedef void (*Pfunc) (lua_State *L, void *ud); + +LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name); +LUAI_FUNC void luaD_callhook (lua_State *L, int event, int line); +LUAI_FUNC int luaD_precall (lua_State *L, StkId func, int nresults); +LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults); +LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u, + ptrdiff_t oldtop, ptrdiff_t ef); +LUAI_FUNC int luaD_poscall (lua_State *L, StkId firstResult); +LUAI_FUNC void luaD_reallocCI (lua_State *L, int newsize); +LUAI_FUNC void luaD_reallocstack (lua_State *L, int newsize); +LUAI_FUNC void luaD_growstack (lua_State *L, int n); + +LUAI_FUNC void luaD_throw (lua_State *L, int errcode); +LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud); + +LUAI_FUNC void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop); + +#endif + diff --git a/lib/lua/ldump.c b/lib/lua/ldump.c new file mode 100644 index 000000000000..c9d3d4870f4d --- /dev/null +++ b/lib/lua/ldump.c @@ -0,0 +1,164 @@ +/* +** $Id: ldump.c,v 2.8.1.1 2007/12/27 13:02:25 roberto Exp $ +** save precompiled Lua chunks +** See Copyright Notice in lua.h +*/ + +#include + +#define ldump_c +#define LUA_CORE + +#include "lua.h" + +#include "lobject.h" +#include "lstate.h" +#include "lundump.h" + +typedef struct { + lua_State* L; + lua_Writer writer; + void* data; + int strip; + int status; +} DumpState; + +#define DumpMem(b,n,size,D) DumpBlock(b,(n)*(size),D) +#define DumpVar(x,D) DumpMem(&x,1,sizeof(x),D) + +static void DumpBlock(const void* b, size_t size, DumpState* D) +{ + if (D->status==0) + { + lua_unlock(D->L); + D->status=(*D->writer)(D->L,b,size,D->data); + lua_lock(D->L); + } +} + +static void DumpChar(int y, DumpState* D) +{ + char x=(char)y; + DumpVar(x,D); +} + +static void DumpInt(int x, DumpState* D) +{ + DumpVar(x,D); +} + +static void DumpNumber(lua_Number x, DumpState* D) +{ + DumpVar(x,D); +} + +static void DumpVector(const void* b, int n, size_t size, DumpState* D) +{ + DumpInt(n,D); + DumpMem(b,n,size,D); +} + +static void DumpString(const TString* s, DumpState* D) +{ + if (s==NULL || getstr(s)==NULL) + { + size_t size=0; + DumpVar(size,D); + } + else + { + size_t size=s->tsv.len+1; /* include trailing '\0' */ + DumpVar(size,D); + DumpBlock(getstr(s),size,D); + } +} + +#define DumpCode(f,D) DumpVector(f->code,f->sizecode,sizeof(Instruction),D) + +static void DumpFunction(const Proto* f, const TString* p, DumpState* D); + +static void DumpConstants(const Proto* f, DumpState* D) +{ + int i,n=f->sizek; + DumpInt(n,D); + for (i=0; ik[i]; + DumpChar(ttype(o),D); + switch (ttype(o)) + { + case LUA_TNIL: + break; + case LUA_TBOOLEAN: + DumpChar(bvalue(o),D); + break; + case LUA_TNUMBER: + DumpNumber(nvalue(o),D); + break; + case LUA_TSTRING: + DumpString(rawtsvalue(o),D); + break; + default: + lua_assert(0); /* cannot happen */ + break; + } + } + n=f->sizep; + DumpInt(n,D); + for (i=0; ip[i],f->source,D); +} + +static void DumpDebug(const Proto* f, DumpState* D) +{ + int i,n; + n= (D->strip) ? 0 : f->sizelineinfo; + DumpVector(f->lineinfo,n,sizeof(int),D); + n= (D->strip) ? 0 : f->sizelocvars; + DumpInt(n,D); + for (i=0; ilocvars[i].varname,D); + DumpInt(f->locvars[i].startpc,D); + DumpInt(f->locvars[i].endpc,D); + } + n= (D->strip) ? 0 : f->sizeupvalues; + DumpInt(n,D); + for (i=0; iupvalues[i],D); +} + +static void DumpFunction(const Proto* f, const TString* p, DumpState* D) +{ + DumpString((f->source==p || D->strip) ? NULL : f->source,D); + DumpInt(f->linedefined,D); + DumpInt(f->lastlinedefined,D); + DumpChar(f->nups,D); + DumpChar(f->numparams,D); + DumpChar(f->is_vararg,D); + DumpChar(f->maxstacksize,D); + DumpCode(f,D); + DumpConstants(f,D); + DumpDebug(f,D); +} + +static void DumpHeader(DumpState* D) +{ + char h[LUAC_HEADERSIZE]; + luaU_header(h); + DumpBlock(h,LUAC_HEADERSIZE,D); +} + +/* +** dump Lua function as precompiled chunk +*/ +int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip) +{ + DumpState D; + D.L=L; + D.writer=w; + D.data=data; + D.strip=strip; + D.status=0; + DumpHeader(&D); + DumpFunction(f,NULL,&D); + return D.status; +} diff --git a/lib/lua/lfunc.c b/lib/lua/lfunc.c new file mode 100644 index 000000000000..813e88f5831b --- /dev/null +++ b/lib/lua/lfunc.c @@ -0,0 +1,174 @@ +/* +** $Id: lfunc.c,v 2.12.1.2 2007/12/28 14:58:43 roberto Exp $ +** Auxiliary functions to manipulate prototypes and closures +** See Copyright Notice in lua.h +*/ + + +#include + +#define lfunc_c +#define LUA_CORE + +#include "lua.h" + +#include "lfunc.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" + + + +Closure *luaF_newCclosure (lua_State *L, int nelems, Table *e) { + Closure *c = cast(Closure *, luaM_malloc(L, sizeCclosure(nelems))); + luaC_link(L, obj2gco(c), LUA_TFUNCTION); + c->c.isC = 1; + c->c.env = e; + c->c.nupvalues = cast_byte(nelems); + return c; +} + + +Closure *luaF_newLclosure (lua_State *L, int nelems, Table *e) { + Closure *c = cast(Closure *, luaM_malloc(L, sizeLclosure(nelems))); + luaC_link(L, obj2gco(c), LUA_TFUNCTION); + c->l.isC = 0; + c->l.env = e; + c->l.nupvalues = cast_byte(nelems); + while (nelems--) c->l.upvals[nelems] = NULL; + return c; +} + + +UpVal *luaF_newupval (lua_State *L) { + UpVal *uv = luaM_new(L, UpVal); + luaC_link(L, obj2gco(uv), LUA_TUPVAL); + uv->v = &uv->u.value; + setnilvalue(uv->v); + return uv; +} + + +UpVal *luaF_findupval (lua_State *L, StkId level) { + global_State *g = G(L); + GCObject **pp = &L->openupval; + UpVal *p; + UpVal *uv; + while (*pp != NULL && (p = ngcotouv(*pp))->v >= level) { + lua_assert(p->v != &p->u.value); + if (p->v == level) { /* found a corresponding upvalue? */ + if (isdead(g, obj2gco(p))) /* is it dead? */ + changewhite(obj2gco(p)); /* ressurect it */ + return p; + } + pp = &p->next; + } + uv = luaM_new(L, UpVal); /* not found: create a new one */ + uv->tt = LUA_TUPVAL; + uv->marked = luaC_white(g); + uv->v = level; /* current value lives in the stack */ + uv->next = *pp; /* chain it in the proper position */ + *pp = obj2gco(uv); + uv->u.l.prev = &g->uvhead; /* double link it in `uvhead' list */ + uv->u.l.next = g->uvhead.u.l.next; + uv->u.l.next->u.l.prev = uv; + g->uvhead.u.l.next = uv; + lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); + return uv; +} + + +static void unlinkupval (UpVal *uv) { + lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); + uv->u.l.next->u.l.prev = uv->u.l.prev; /* remove from `uvhead' list */ + uv->u.l.prev->u.l.next = uv->u.l.next; +} + + +void luaF_freeupval (lua_State *L, UpVal *uv) { + if (uv->v != &uv->u.value) /* is it open? */ + unlinkupval(uv); /* remove from open list */ + luaM_free(L, uv); /* free upvalue */ +} + + +void luaF_close (lua_State *L, StkId level) { + UpVal *uv; + global_State *g = G(L); + while (L->openupval != NULL && (uv = ngcotouv(L->openupval))->v >= level) { + GCObject *o = obj2gco(uv); + lua_assert(!isblack(o) && uv->v != &uv->u.value); + L->openupval = uv->next; /* remove from `open' list */ + if (isdead(g, o)) + luaF_freeupval(L, uv); /* free upvalue */ + else { + unlinkupval(uv); + setobj(L, &uv->u.value, uv->v); + uv->v = &uv->u.value; /* now current value lives here */ + luaC_linkupval(L, uv); /* link upvalue into `gcroot' list */ + } + } +} + + +Proto *luaF_newproto (lua_State *L) { + Proto *f = luaM_new(L, Proto); + luaC_link(L, obj2gco(f), LUA_TPROTO); + f->k = NULL; + f->sizek = 0; + f->p = NULL; + f->sizep = 0; + f->code = NULL; + f->sizecode = 0; + f->sizelineinfo = 0; + f->sizeupvalues = 0; + f->nups = 0; + f->upvalues = NULL; + f->numparams = 0; + f->is_vararg = 0; + f->maxstacksize = 0; + f->lineinfo = NULL; + f->sizelocvars = 0; + f->locvars = NULL; + f->linedefined = 0; + f->lastlinedefined = 0; + f->source = NULL; + return f; +} + + +void luaF_freeproto (lua_State *L, Proto *f) { + luaM_freearray(L, f->code, f->sizecode, Instruction); + luaM_freearray(L, f->p, f->sizep, Proto *); + luaM_freearray(L, f->k, f->sizek, TValue); + luaM_freearray(L, f->lineinfo, f->sizelineinfo, int); + luaM_freearray(L, f->locvars, f->sizelocvars, struct LocVar); + luaM_freearray(L, f->upvalues, f->sizeupvalues, TString *); + luaM_free(L, f); +} + + +void luaF_freeclosure (lua_State *L, Closure *c) { + int size = (c->c.isC) ? sizeCclosure(c->c.nupvalues) : + sizeLclosure(c->l.nupvalues); + luaM_freemem(L, c, size); +} + + +/* +** Look for n-th local variable at line `line' in function `func'. +** Returns NULL if not found. +*/ +const char *luaF_getlocalname (const Proto *f, int local_number, int pc) { + int i; + for (i = 0; isizelocvars && f->locvars[i].startpc <= pc; i++) { + if (pc < f->locvars[i].endpc) { /* is variable active? */ + local_number--; + if (local_number == 0) + return getstr(f->locvars[i].varname); + } + } + return NULL; /* not found */ +} + diff --git a/lib/lua/lfunc.h b/lib/lua/lfunc.h new file mode 100644 index 000000000000..a68cf5151cba --- /dev/null +++ b/lib/lua/lfunc.h @@ -0,0 +1,34 @@ +/* +** $Id: lfunc.h,v 2.4.1.1 2007/12/27 13:02:25 roberto Exp $ +** Auxiliary functions to manipulate prototypes and closures +** See Copyright Notice in lua.h +*/ + +#ifndef lfunc_h +#define lfunc_h + + +#include "lobject.h" + + +#define sizeCclosure(n) (cast(int, sizeof(CClosure)) + \ + cast(int, sizeof(TValue)*((n)-1))) + +#define sizeLclosure(n) (cast(int, sizeof(LClosure)) + \ + cast(int, sizeof(TValue *)*((n)-1))) + + +LUAI_FUNC Proto *luaF_newproto (lua_State *L); +LUAI_FUNC Closure *luaF_newCclosure (lua_State *L, int nelems, Table *e); +LUAI_FUNC Closure *luaF_newLclosure (lua_State *L, int nelems, Table *e); +LUAI_FUNC UpVal *luaF_newupval (lua_State *L); +LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level); +LUAI_FUNC void luaF_close (lua_State *L, StkId level); +LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f); +LUAI_FUNC void luaF_freeclosure (lua_State *L, Closure *c); +LUAI_FUNC void luaF_freeupval (lua_State *L, UpVal *uv); +LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number, + int pc); + + +#endif diff --git a/lib/lua/lgc.c b/lib/lua/lgc.c new file mode 100644 index 000000000000..e909c79a9696 --- /dev/null +++ b/lib/lua/lgc.c @@ -0,0 +1,710 @@ +/* +** $Id: lgc.c,v 2.38.1.2 2011/03/18 18:05:38 roberto Exp $ +** Garbage Collector +** See Copyright Notice in lua.h +*/ + +#include + +#define lgc_c +#define LUA_CORE + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" + + +#define GCSTEPSIZE 1024u +#define GCSWEEPMAX 40 +#define GCSWEEPCOST 10 +#define GCFINALIZECOST 100 + + +#define maskmarks cast_byte(~(bitmask(BLACKBIT)|WHITEBITS)) + +#define makewhite(g,x) \ + ((x)->gch.marked = cast_byte(((x)->gch.marked & maskmarks) | luaC_white(g))) + +#define white2gray(x) reset2bits((x)->gch.marked, WHITE0BIT, WHITE1BIT) +#define black2gray(x) resetbit((x)->gch.marked, BLACKBIT) + +#define stringmark(s) reset2bits((s)->tsv.marked, WHITE0BIT, WHITE1BIT) + + +#define isfinalized(u) testbit((u)->marked, FINALIZEDBIT) +#define markfinalized(u) l_setbit((u)->marked, FINALIZEDBIT) + + +#define KEYWEAK bitmask(KEYWEAKBIT) +#define VALUEWEAK bitmask(VALUEWEAKBIT) + + + +#define markvalue(g,o) { checkconsistency(o); \ + if (iscollectable(o) && iswhite(gcvalue(o))) reallymarkobject(g,gcvalue(o)); } + +#define markobject(g,t) { if (iswhite(obj2gco(t))) \ + reallymarkobject(g, obj2gco(t)); } + + +#define setthreshold(g) (g->GCthreshold = (g->estimate/100) * g->gcpause) + + +static void removeentry (Node *n) { + lua_assert(ttisnil(gval(n))); + if (iscollectable(gkey(n))) + setttype(gkey(n), LUA_TDEADKEY); /* dead key; remove it */ +} + + +static void reallymarkobject (global_State *g, GCObject *o) { + lua_assert(iswhite(o) && !isdead(g, o)); + white2gray(o); + switch (o->gch.tt) { + case LUA_TSTRING: { + return; + } + case LUA_TUSERDATA: { + Table *mt = gco2u(o)->metatable; + gray2black(o); /* udata are never gray */ + if (mt) markobject(g, mt); + markobject(g, gco2u(o)->env); + return; + } + case LUA_TUPVAL: { + UpVal *uv = gco2uv(o); + markvalue(g, uv->v); + if (uv->v == &uv->u.value) /* closed? */ + gray2black(o); /* open upvalues are never black */ + return; + } + case LUA_TFUNCTION: { + gco2cl(o)->c.gclist = g->gray; + g->gray = o; + break; + } + case LUA_TTABLE: { + gco2h(o)->gclist = g->gray; + g->gray = o; + break; + } + case LUA_TTHREAD: { + gco2th(o)->gclist = g->gray; + g->gray = o; + break; + } + case LUA_TPROTO: { + gco2p(o)->gclist = g->gray; + g->gray = o; + break; + } + default: lua_assert(0); + } +} + + +static void marktmu (global_State *g) { + GCObject *u = g->tmudata; + if (u) { + do { + u = u->gch.next; + makewhite(g, u); /* may be marked, if left from previous GC */ + reallymarkobject(g, u); + } while (u != g->tmudata); + } +} + + +/* move `dead' udata that need finalization to list `tmudata' */ +size_t luaC_separateudata (lua_State *L, int all) { + global_State *g = G(L); + size_t deadmem = 0; + GCObject **p = &g->mainthread->next; + GCObject *curr; + while ((curr = *p) != NULL) { + if (!(iswhite(curr) || all) || isfinalized(gco2u(curr))) + p = &curr->gch.next; /* don't bother with them */ + else if (fasttm(L, gco2u(curr)->metatable, TM_GC) == NULL) { + markfinalized(gco2u(curr)); /* don't need finalization */ + p = &curr->gch.next; + } + else { /* must call its gc method */ + deadmem += sizeudata(gco2u(curr)); + markfinalized(gco2u(curr)); + *p = curr->gch.next; + /* link `curr' at the end of `tmudata' list */ + if (g->tmudata == NULL) /* list is empty? */ + g->tmudata = curr->gch.next = curr; /* creates a circular list */ + else { + curr->gch.next = g->tmudata->gch.next; + g->tmudata->gch.next = curr; + g->tmudata = curr; + } + } + } + return deadmem; +} + + +static int traversetable (global_State *g, Table *h) { + int i; + int weakkey = 0; + int weakvalue = 0; + const TValue *mode; + if (h->metatable) + markobject(g, h->metatable); + mode = gfasttm(g, h->metatable, TM_MODE); + if (mode && ttisstring(mode)) { /* is there a weak mode? */ + weakkey = (strchr(svalue(mode), 'k') != NULL); + weakvalue = (strchr(svalue(mode), 'v') != NULL); + if (weakkey || weakvalue) { /* is really weak? */ + h->marked &= ~(KEYWEAK | VALUEWEAK); /* clear bits */ + h->marked |= cast_byte((weakkey << KEYWEAKBIT) | + (weakvalue << VALUEWEAKBIT)); + h->gclist = g->weak; /* must be cleared after GC, ... */ + g->weak = obj2gco(h); /* ... so put in the appropriate list */ + } + } + if (weakkey && weakvalue) return 1; + if (!weakvalue) { + i = h->sizearray; + while (i--) + markvalue(g, &h->array[i]); + } + i = sizenode(h); + while (i--) { + Node *n = gnode(h, i); + lua_assert(ttype(gkey(n)) != LUA_TDEADKEY || ttisnil(gval(n))); + if (ttisnil(gval(n))) + removeentry(n); /* remove empty entries */ + else { + lua_assert(!ttisnil(gkey(n))); + if (!weakkey) markvalue(g, gkey(n)); + if (!weakvalue) markvalue(g, gval(n)); + } + } + return weakkey || weakvalue; +} + + +/* +** All marks are conditional because a GC may happen while the +** prototype is still being created +*/ +static void traverseproto (global_State *g, Proto *f) { + int i; + if (f->source) stringmark(f->source); + for (i=0; isizek; i++) /* mark literals */ + markvalue(g, &f->k[i]); + for (i=0; isizeupvalues; i++) { /* mark upvalue names */ + if (f->upvalues[i]) + stringmark(f->upvalues[i]); + } + for (i=0; isizep; i++) { /* mark nested protos */ + if (f->p[i]) + markobject(g, f->p[i]); + } + for (i=0; isizelocvars; i++) { /* mark local-variable names */ + if (f->locvars[i].varname) + stringmark(f->locvars[i].varname); + } +} + + + +static void traverseclosure (global_State *g, Closure *cl) { + markobject(g, cl->c.env); + if (cl->c.isC) { + int i; + for (i=0; ic.nupvalues; i++) /* mark its upvalues */ + markvalue(g, &cl->c.upvalue[i]); + } + else { + int i; + lua_assert(cl->l.nupvalues == cl->l.p->nups); + markobject(g, cl->l.p); + for (i=0; il.nupvalues; i++) /* mark its upvalues */ + markobject(g, cl->l.upvals[i]); + } +} + + +static void checkstacksizes (lua_State *L, StkId max) { + int ci_used = cast_int(L->ci - L->base_ci); /* number of `ci' in use */ + int s_used = cast_int(max - L->stack); /* part of stack in use */ + if (L->size_ci > LUAI_MAXCALLS) /* handling overflow? */ + return; /* do not touch the stacks */ + if (4*ci_used < L->size_ci && 2*BASIC_CI_SIZE < L->size_ci) + luaD_reallocCI(L, L->size_ci/2); /* still big enough... */ + condhardstacktests(luaD_reallocCI(L, ci_used + 1)); + if (4*s_used < L->stacksize && + 2*(BASIC_STACK_SIZE+EXTRA_STACK) < L->stacksize) + luaD_reallocstack(L, L->stacksize/2); /* still big enough... */ + condhardstacktests(luaD_reallocstack(L, s_used)); +} + + +static void traversestack (global_State *g, lua_State *l) { + StkId o, lim; + CallInfo *ci; + markvalue(g, gt(l)); + lim = l->top; + for (ci = l->base_ci; ci <= l->ci; ci++) { + lua_assert(ci->top <= l->stack_last); + if (lim < ci->top) lim = ci->top; + } + for (o = l->stack; o < l->top; o++) + markvalue(g, o); + for (; o <= lim; o++) + setnilvalue(o); + checkstacksizes(l, lim); +} + + +/* +** traverse one gray object, turning it to black. +** Returns `quantity' traversed. +*/ +static l_mem propagatemark (global_State *g) { + GCObject *o = g->gray; + lua_assert(isgray(o)); + gray2black(o); + switch (o->gch.tt) { + case LUA_TTABLE: { + Table *h = gco2h(o); + g->gray = h->gclist; + if (traversetable(g, h)) /* table is weak? */ + black2gray(o); /* keep it gray */ + return sizeof(Table) + sizeof(TValue) * h->sizearray + + sizeof(Node) * sizenode(h); + } + case LUA_TFUNCTION: { + Closure *cl = gco2cl(o); + g->gray = cl->c.gclist; + traverseclosure(g, cl); + return (cl->c.isC) ? sizeCclosure(cl->c.nupvalues) : + sizeLclosure(cl->l.nupvalues); + } + case LUA_TTHREAD: { + lua_State *th = gco2th(o); + g->gray = th->gclist; + th->gclist = g->grayagain; + g->grayagain = o; + black2gray(o); + traversestack(g, th); + return sizeof(lua_State) + sizeof(TValue) * th->stacksize + + sizeof(CallInfo) * th->size_ci; + } + case LUA_TPROTO: { + Proto *p = gco2p(o); + g->gray = p->gclist; + traverseproto(g, p); + return sizeof(Proto) + sizeof(Instruction) * p->sizecode + + sizeof(Proto *) * p->sizep + + sizeof(TValue) * p->sizek + + sizeof(int) * p->sizelineinfo + + sizeof(LocVar) * p->sizelocvars + + sizeof(TString *) * p->sizeupvalues; + } + default: lua_assert(0); return 0; + } +} + + +static size_t propagateall (global_State *g) { + size_t m = 0; + while (g->gray) m += propagatemark(g); + return m; +} + + +/* +** The next function tells whether a key or value can be cleared from +** a weak table. Non-collectable objects are never removed from weak +** tables. Strings behave as `values', so are never removed too. for +** other objects: if really collected, cannot keep them; for userdata +** being finalized, keep them in keys, but not in values +*/ +static int iscleared (const TValue *o, int iskey) { + if (!iscollectable(o)) return 0; + if (ttisstring(o)) { + stringmark(rawtsvalue(o)); /* strings are `values', so are never weak */ + return 0; + } + return iswhite(gcvalue(o)) || + (ttisuserdata(o) && (!iskey && isfinalized(uvalue(o)))); +} + + +/* +** clear collected entries from weaktables +*/ +static void cleartable (GCObject *l) { + while (l) { + Table *h = gco2h(l); + int i = h->sizearray; + lua_assert(testbit(h->marked, VALUEWEAKBIT) || + testbit(h->marked, KEYWEAKBIT)); + if (testbit(h->marked, VALUEWEAKBIT)) { + while (i--) { + TValue *o = &h->array[i]; + if (iscleared(o, 0)) /* value was collected? */ + setnilvalue(o); /* remove value */ + } + } + i = sizenode(h); + while (i--) { + Node *n = gnode(h, i); + if (!ttisnil(gval(n)) && /* non-empty entry? */ + (iscleared(key2tval(n), 1) || iscleared(gval(n), 0))) { + setnilvalue(gval(n)); /* remove value ... */ + removeentry(n); /* remove entry from table */ + } + } + l = h->gclist; + } +} + + +static void freeobj (lua_State *L, GCObject *o) { + switch (o->gch.tt) { + case LUA_TPROTO: luaF_freeproto(L, gco2p(o)); break; + case LUA_TFUNCTION: luaF_freeclosure(L, gco2cl(o)); break; + case LUA_TUPVAL: luaF_freeupval(L, gco2uv(o)); break; + case LUA_TTABLE: luaH_free(L, gco2h(o)); break; + case LUA_TTHREAD: { + lua_assert(gco2th(o) != L && gco2th(o) != G(L)->mainthread); + luaE_freethread(L, gco2th(o)); + break; + } + case LUA_TSTRING: { + G(L)->strt.nuse--; + luaM_freemem(L, o, sizestring(gco2ts(o))); + break; + } + case LUA_TUSERDATA: { + luaM_freemem(L, o, sizeudata(gco2u(o))); + break; + } + default: lua_assert(0); + } +} + + + +#define sweepwholelist(L,p) sweeplist(L,p,MAX_LUMEM) + + +static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) { + GCObject *curr; + global_State *g = G(L); + int deadmask = otherwhite(g); + while ((curr = *p) != NULL && count-- > 0) { + if (curr->gch.tt == LUA_TTHREAD) /* sweep open upvalues of each thread */ + sweepwholelist(L, &gco2th(curr)->openupval); + if ((curr->gch.marked ^ WHITEBITS) & deadmask) { /* not dead? */ + lua_assert(!isdead(g, curr) || testbit(curr->gch.marked, FIXEDBIT)); + makewhite(g, curr); /* make it white (for next cycle) */ + p = &curr->gch.next; + } + else { /* must erase `curr' */ + lua_assert(isdead(g, curr) || deadmask == bitmask(SFIXEDBIT)); + *p = curr->gch.next; + if (curr == g->rootgc) /* is the first element of the list? */ + g->rootgc = curr->gch.next; /* adjust first */ + freeobj(L, curr); + } + } + return p; +} + + +static void checkSizes (lua_State *L) { + global_State *g = G(L); + /* check size of string hash */ + if (g->strt.nuse < cast(lu_int32, g->strt.size/4) && + g->strt.size > MINSTRTABSIZE*2) + luaS_resize(L, g->strt.size/2); /* table is too big */ + /* check size of buffer */ + if (luaZ_sizebuffer(&g->buff) > LUA_MINBUFFER*2) { /* buffer too big? */ + size_t newsize = luaZ_sizebuffer(&g->buff) / 2; + luaZ_resizebuffer(L, &g->buff, newsize); + } +} + + +static void GCTM (lua_State *L) { + global_State *g = G(L); + GCObject *o = g->tmudata->gch.next; /* get first element */ + Udata *udata = rawgco2u(o); + const TValue *tm; + /* remove udata from `tmudata' */ + if (o == g->tmudata) /* last element? */ + g->tmudata = NULL; + else + g->tmudata->gch.next = udata->uv.next; + udata->uv.next = g->mainthread->next; /* return it to `root' list */ + g->mainthread->next = o; + makewhite(g, o); + tm = fasttm(L, udata->uv.metatable, TM_GC); + if (tm != NULL) { + lu_byte oldah = L->allowhook; + lu_mem oldt = g->GCthreshold; + L->allowhook = 0; /* stop debug hooks during GC tag method */ + g->GCthreshold = 2*g->totalbytes; /* avoid GC steps */ + setobj2s(L, L->top, tm); + setuvalue(L, L->top+1, udata); + L->top += 2; + luaD_call(L, L->top - 2, 0); + L->allowhook = oldah; /* restore hooks */ + g->GCthreshold = oldt; /* restore threshold */ + } +} + + +/* +** Call all GC tag methods +*/ +void luaC_callGCTM (lua_State *L) { + while (G(L)->tmudata) + GCTM(L); +} + + +void luaC_freeall (lua_State *L) { + global_State *g = G(L); + int i; + g->currentwhite = WHITEBITS | bitmask(SFIXEDBIT); /* mask to collect all elements */ + sweepwholelist(L, &g->rootgc); + for (i = 0; i < g->strt.size; i++) /* free all string lists */ + sweepwholelist(L, &g->strt.hash[i]); +} + + +static void markmt (global_State *g) { + int i; + for (i=0; imt[i]) markobject(g, g->mt[i]); +} + + +/* mark root set */ +static void markroot (lua_State *L) { + global_State *g = G(L); + g->gray = NULL; + g->grayagain = NULL; + g->weak = NULL; + markobject(g, g->mainthread); + /* make global table be traversed before main stack */ + markvalue(g, gt(g->mainthread)); + markvalue(g, registry(L)); + markmt(g); + g->gcstate = GCSpropagate; +} + + +static void remarkupvals (global_State *g) { + UpVal *uv; + for (uv = g->uvhead.u.l.next; uv != &g->uvhead; uv = uv->u.l.next) { + lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); + if (isgray(obj2gco(uv))) + markvalue(g, uv->v); + } +} + + +static void atomic (lua_State *L) { + global_State *g = G(L); + size_t udsize; /* total size of userdata to be finalized */ + /* remark occasional upvalues of (maybe) dead threads */ + remarkupvals(g); + /* traverse objects cautch by write barrier and by 'remarkupvals' */ + propagateall(g); + /* remark weak tables */ + g->gray = g->weak; + g->weak = NULL; + lua_assert(!iswhite(obj2gco(g->mainthread))); + markobject(g, L); /* mark running thread */ + markmt(g); /* mark basic metatables (again) */ + propagateall(g); + /* remark gray again */ + g->gray = g->grayagain; + g->grayagain = NULL; + propagateall(g); + udsize = luaC_separateudata(L, 0); /* separate userdata to be finalized */ + marktmu(g); /* mark `preserved' userdata */ + udsize += propagateall(g); /* remark, to propagate `preserveness' */ + cleartable(g->weak); /* remove collected objects from weak tables */ + /* flip current white */ + g->currentwhite = cast_byte(otherwhite(g)); + g->sweepstrgc = 0; + g->sweepgc = &g->rootgc; + g->gcstate = GCSsweepstring; + g->estimate = g->totalbytes - udsize; /* first estimate */ +} + + +static l_mem singlestep (lua_State *L) { + global_State *g = G(L); + /*lua_checkmemory(L);*/ + switch (g->gcstate) { + case GCSpause: { + markroot(L); /* start a new collection */ + return 0; + } + case GCSpropagate: { + if (g->gray) + return propagatemark(g); + else { /* no more `gray' objects */ + atomic(L); /* finish mark phase */ + return 0; + } + } + case GCSsweepstring: { + lu_mem old = g->totalbytes; + sweepwholelist(L, &g->strt.hash[g->sweepstrgc++]); + if (g->sweepstrgc >= g->strt.size) /* nothing more to sweep? */ + g->gcstate = GCSsweep; /* end sweep-string phase */ + lua_assert(old >= g->totalbytes); + g->estimate -= old - g->totalbytes; + return GCSWEEPCOST; + } + case GCSsweep: { + lu_mem old = g->totalbytes; + g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX); + if (*g->sweepgc == NULL) { /* nothing more to sweep? */ + checkSizes(L); + g->gcstate = GCSfinalize; /* end sweep phase */ + } + lua_assert(old >= g->totalbytes); + g->estimate -= old - g->totalbytes; + return GCSWEEPMAX*GCSWEEPCOST; + } + case GCSfinalize: { + if (g->tmudata) { + GCTM(L); + if (g->estimate > GCFINALIZECOST) + g->estimate -= GCFINALIZECOST; + return GCFINALIZECOST; + } + else { + g->gcstate = GCSpause; /* end collection */ + g->gcdept = 0; + return 0; + } + } + default: lua_assert(0); return 0; + } +} + + +void luaC_step (lua_State *L) { + global_State *g = G(L); + l_mem lim = (GCSTEPSIZE/100) * g->gcstepmul; + if (lim == 0) + lim = (MAX_LUMEM-1)/2; /* no limit */ + g->gcdept += g->totalbytes - g->GCthreshold; + do { + lim -= singlestep(L); + if (g->gcstate == GCSpause) + break; + } while (lim > 0); + if (g->gcstate != GCSpause) { + if (g->gcdept < GCSTEPSIZE) + g->GCthreshold = g->totalbytes + GCSTEPSIZE; /* - lim/g->gcstepmul;*/ + else { + g->gcdept -= GCSTEPSIZE; + g->GCthreshold = g->totalbytes; + } + } + else { + setthreshold(g); + } +} + + +void luaC_fullgc (lua_State *L) { + global_State *g = G(L); + if (g->gcstate <= GCSpropagate) { + /* reset sweep marks to sweep all elements (returning them to white) */ + g->sweepstrgc = 0; + g->sweepgc = &g->rootgc; + /* reset other collector lists */ + g->gray = NULL; + g->grayagain = NULL; + g->weak = NULL; + g->gcstate = GCSsweepstring; + } + lua_assert(g->gcstate != GCSpause && g->gcstate != GCSpropagate); + /* finish any pending sweep phase */ + while (g->gcstate != GCSfinalize) { + lua_assert(g->gcstate == GCSsweepstring || g->gcstate == GCSsweep); + singlestep(L); + } + markroot(L); + while (g->gcstate != GCSpause) { + singlestep(L); + } + setthreshold(g); +} + + +void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v) { + global_State *g = G(L); + lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); + lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); + lua_assert(ttype(&o->gch) != LUA_TTABLE); + /* must keep invariant? */ + if (g->gcstate == GCSpropagate) + reallymarkobject(g, v); /* restore invariant */ + else /* don't mind */ + makewhite(g, o); /* mark as white just to avoid other barriers */ +} + + +void luaC_barrierback (lua_State *L, Table *t) { + global_State *g = G(L); + GCObject *o = obj2gco(t); + lua_assert(isblack(o) && !isdead(g, o)); + lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); + black2gray(o); /* make table gray (again) */ + t->gclist = g->grayagain; + g->grayagain = o; +} + + +void luaC_link (lua_State *L, GCObject *o, lu_byte tt) { + global_State *g = G(L); + o->gch.next = g->rootgc; + g->rootgc = o; + o->gch.marked = luaC_white(g); + o->gch.tt = tt; +} + + +void luaC_linkupval (lua_State *L, UpVal *uv) { + global_State *g = G(L); + GCObject *o = obj2gco(uv); + o->gch.next = g->rootgc; /* link upvalue into `rootgc' list */ + g->rootgc = o; + if (isgray(o)) { + if (g->gcstate == GCSpropagate) { + gray2black(o); /* closed upvalues need barrier */ + luaC_barrier(L, uv, uv->v); + } + else { /* sweep phase: sweep it (turning it into white) */ + makewhite(g, o); + lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); + } + } +} + diff --git a/lib/lua/lgc.h b/lib/lua/lgc.h new file mode 100644 index 000000000000..5a8dc605b319 --- /dev/null +++ b/lib/lua/lgc.h @@ -0,0 +1,110 @@ +/* +** $Id: lgc.h,v 2.15.1.1 2007/12/27 13:02:25 roberto Exp $ +** Garbage Collector +** See Copyright Notice in lua.h +*/ + +#ifndef lgc_h +#define lgc_h + + +#include "lobject.h" + + +/* +** Possible states of the Garbage Collector +*/ +#define GCSpause 0 +#define GCSpropagate 1 +#define GCSsweepstring 2 +#define GCSsweep 3 +#define GCSfinalize 4 + + +/* +** some userful bit tricks +*/ +#define resetbits(x,m) ((x) &= cast(lu_byte, ~(m))) +#define setbits(x,m) ((x) |= (m)) +#define testbits(x,m) ((x) & (m)) +#define bitmask(b) (1<<(b)) +#define bit2mask(b1,b2) (bitmask(b1) | bitmask(b2)) +#define l_setbit(x,b) setbits(x, bitmask(b)) +#define resetbit(x,b) resetbits(x, bitmask(b)) +#define testbit(x,b) testbits(x, bitmask(b)) +#define set2bits(x,b1,b2) setbits(x, (bit2mask(b1, b2))) +#define reset2bits(x,b1,b2) resetbits(x, (bit2mask(b1, b2))) +#define test2bits(x,b1,b2) testbits(x, (bit2mask(b1, b2))) + + + +/* +** Layout for bit use in `marked' field: +** bit 0 - object is white (type 0) +** bit 1 - object is white (type 1) +** bit 2 - object is black +** bit 3 - for userdata: has been finalized +** bit 3 - for tables: has weak keys +** bit 4 - for tables: has weak values +** bit 5 - object is fixed (should not be collected) +** bit 6 - object is "super" fixed (only the main thread) +*/ + + +#define WHITE0BIT 0 +#define WHITE1BIT 1 +#define BLACKBIT 2 +#define FINALIZEDBIT 3 +#define KEYWEAKBIT 3 +#define VALUEWEAKBIT 4 +#define FIXEDBIT 5 +#define SFIXEDBIT 6 +#define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT) + + +#define iswhite(x) test2bits((x)->gch.marked, WHITE0BIT, WHITE1BIT) +#define isblack(x) testbit((x)->gch.marked, BLACKBIT) +#define isgray(x) (!isblack(x) && !iswhite(x)) + +#define otherwhite(g) (g->currentwhite ^ WHITEBITS) +#define isdead(g,v) ((v)->gch.marked & otherwhite(g) & WHITEBITS) + +#define changewhite(x) ((x)->gch.marked ^= WHITEBITS) +#define gray2black(x) l_setbit((x)->gch.marked, BLACKBIT) + +#define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x))) + +#define luaC_white(g) cast(lu_byte, (g)->currentwhite & WHITEBITS) + + +#define luaC_checkGC(L) { \ + condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1)); \ + if (G(L)->totalbytes >= G(L)->GCthreshold) \ + luaC_step(L); } + + +#define luaC_barrier(L,p,v) { if (valiswhite(v) && isblack(obj2gco(p))) \ + luaC_barrierf(L,obj2gco(p),gcvalue(v)); } + +#define luaC_barriert(L,t,v) { if (valiswhite(v) && isblack(obj2gco(t))) \ + luaC_barrierback(L,t); } + +#define luaC_objbarrier(L,p,o) \ + { if (iswhite(obj2gco(o)) && isblack(obj2gco(p))) \ + luaC_barrierf(L,obj2gco(p),obj2gco(o)); } + +#define luaC_objbarriert(L,t,o) \ + { if (iswhite(obj2gco(o)) && isblack(obj2gco(t))) luaC_barrierback(L,t); } + +LUAI_FUNC size_t luaC_separateudata (lua_State *L, int all); +LUAI_FUNC void luaC_callGCTM (lua_State *L); +LUAI_FUNC void luaC_freeall (lua_State *L); +LUAI_FUNC void luaC_step (lua_State *L); +LUAI_FUNC void luaC_fullgc (lua_State *L); +LUAI_FUNC void luaC_link (lua_State *L, GCObject *o, lu_byte tt); +LUAI_FUNC void luaC_linkupval (lua_State *L, UpVal *uv); +LUAI_FUNC void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v); +LUAI_FUNC void luaC_barrierback (lua_State *L, Table *t); + + +#endif diff --git a/lib/lua/linit.c b/lib/lua/linit.c new file mode 100644 index 000000000000..c1f90dfab715 --- /dev/null +++ b/lib/lua/linit.c @@ -0,0 +1,38 @@ +/* +** $Id: linit.c,v 1.14.1.1 2007/12/27 13:02:25 roberto Exp $ +** Initialization of libraries for lua.c +** See Copyright Notice in lua.h +*/ + + +#define linit_c +#define LUA_LIB + +#include "lua.h" + +#include "lualib.h" +#include "lauxlib.h" + + +static const luaL_Reg lualibs[] = { + {"", luaopen_base}, + {LUA_LOADLIBNAME, luaopen_package}, + {LUA_TABLIBNAME, luaopen_table}, + {LUA_IOLIBNAME, luaopen_io}, + {LUA_OSLIBNAME, luaopen_os}, + {LUA_STRLIBNAME, luaopen_string}, + {LUA_MATHLIBNAME, luaopen_math}, + {LUA_DBLIBNAME, luaopen_debug}, + {NULL, NULL} +}; + + +LUALIB_API void luaL_openlibs (lua_State *L) { + const luaL_Reg *lib = lualibs; + for (; lib->func; lib++) { + lua_pushcfunction(L, lib->func); + lua_pushstring(L, lib->name); + lua_call(L, 1, 0); + } +} + diff --git a/lib/lua/liolib.c b/lib/lua/liolib.c new file mode 100644 index 000000000000..649f9a595155 --- /dev/null +++ b/lib/lua/liolib.c @@ -0,0 +1,556 @@ +/* +** $Id: liolib.c,v 2.73.1.4 2010/05/14 15:33:51 roberto Exp $ +** Standard I/O (and system) library +** See Copyright Notice in lua.h +*/ + + +#include +#include +#include +#include + +#define liolib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + + +#define IO_INPUT 1 +#define IO_OUTPUT 2 + + +static const char *const fnames[] = {"input", "output"}; + + +static int pushresult (lua_State *L, int i, const char *filename) { + int en = errno; /* calls to Lua API may change this value */ + if (i) { + lua_pushboolean(L, 1); + return 1; + } + else { + lua_pushnil(L); + if (filename) + lua_pushfstring(L, "%s: %s", filename, strerror(en)); + else + lua_pushfstring(L, "%s", strerror(en)); + lua_pushinteger(L, en); + return 3; + } +} + + +static void fileerror (lua_State *L, int arg, const char *filename) { + lua_pushfstring(L, "%s: %s", filename, strerror(errno)); + luaL_argerror(L, arg, lua_tostring(L, -1)); +} + + +#define tofilep(L) ((FILE **)luaL_checkudata(L, 1, LUA_FILEHANDLE)) + + +static int io_type (lua_State *L) { + void *ud; + luaL_checkany(L, 1); + ud = lua_touserdata(L, 1); + lua_getfield(L, LUA_REGISTRYINDEX, LUA_FILEHANDLE); + if (ud == NULL || !lua_getmetatable(L, 1) || !lua_rawequal(L, -2, -1)) + lua_pushnil(L); /* not a file */ + else if (*((FILE **)ud) == NULL) + lua_pushliteral(L, "closed file"); + else + lua_pushliteral(L, "file"); + return 1; +} + + +static FILE *tofile (lua_State *L) { + FILE **f = tofilep(L); + if (*f == NULL) + luaL_error(L, "attempt to use a closed file"); + return *f; +} + + + +/* +** When creating file handles, always creates a `closed' file handle +** before opening the actual file; so, if there is a memory error, the +** file is not left opened. +*/ +static FILE **newfile (lua_State *L) { + FILE **pf = (FILE **)lua_newuserdata(L, sizeof(FILE *)); + *pf = NULL; /* file handle is currently `closed' */ + luaL_getmetatable(L, LUA_FILEHANDLE); + lua_setmetatable(L, -2); + return pf; +} + + +/* +** function to (not) close the standard files stdin, stdout, and stderr +*/ +static int io_noclose (lua_State *L) { + lua_pushnil(L); + lua_pushliteral(L, "cannot close standard file"); + return 2; +} + + +/* +** function to close 'popen' files +*/ +static int io_pclose (lua_State *L) { + FILE **p = tofilep(L); + int ok = lua_pclose(L, *p); + *p = NULL; + return pushresult(L, ok, NULL); +} + + +/* +** function to close regular files +*/ +static int io_fclose (lua_State *L) { + FILE **p = tofilep(L); + int ok = (fclose(*p) == 0); + *p = NULL; + return pushresult(L, ok, NULL); +} + + +static int aux_close (lua_State *L) { + lua_getfenv(L, 1); + lua_getfield(L, -1, "__close"); + return (lua_tocfunction(L, -1))(L); +} + + +static int io_close (lua_State *L) { + if (lua_isnone(L, 1)) + lua_rawgeti(L, LUA_ENVIRONINDEX, IO_OUTPUT); + tofile(L); /* make sure argument is a file */ + return aux_close(L); +} + + +static int io_gc (lua_State *L) { + FILE *f = *tofilep(L); + /* ignore closed files */ + if (f != NULL) + aux_close(L); + return 0; +} + + +static int io_tostring (lua_State *L) { + FILE *f = *tofilep(L); + if (f == NULL) + lua_pushliteral(L, "file (closed)"); + else + lua_pushfstring(L, "file (%p)", f); + return 1; +} + + +static int io_open (lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + const char *mode = luaL_optstring(L, 2, "r"); + FILE **pf = newfile(L); + *pf = fopen(filename, mode); + return (*pf == NULL) ? pushresult(L, 0, filename) : 1; +} + + +/* +** this function has a separated environment, which defines the +** correct __close for 'popen' files +*/ +static int io_popen (lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + const char *mode = luaL_optstring(L, 2, "r"); + FILE **pf = newfile(L); + *pf = lua_popen(L, filename, mode); + return (*pf == NULL) ? pushresult(L, 0, filename) : 1; +} + + +static int io_tmpfile (lua_State *L) { + FILE **pf = newfile(L); + *pf = tmpfile(); + return (*pf == NULL) ? pushresult(L, 0, NULL) : 1; +} + + +static FILE *getiofile (lua_State *L, int findex) { + FILE *f; + lua_rawgeti(L, LUA_ENVIRONINDEX, findex); + f = *(FILE **)lua_touserdata(L, -1); + if (f == NULL) + luaL_error(L, "standard %s file is closed", fnames[findex - 1]); + return f; +} + + +static int g_iofile (lua_State *L, int f, const char *mode) { + if (!lua_isnoneornil(L, 1)) { + const char *filename = lua_tostring(L, 1); + if (filename) { + FILE **pf = newfile(L); + *pf = fopen(filename, mode); + if (*pf == NULL) + fileerror(L, 1, filename); + } + else { + tofile(L); /* check that it's a valid file handle */ + lua_pushvalue(L, 1); + } + lua_rawseti(L, LUA_ENVIRONINDEX, f); + } + /* return current value */ + lua_rawgeti(L, LUA_ENVIRONINDEX, f); + return 1; +} + + +static int io_input (lua_State *L) { + return g_iofile(L, IO_INPUT, "r"); +} + + +static int io_output (lua_State *L) { + return g_iofile(L, IO_OUTPUT, "w"); +} + + +static int io_readline (lua_State *L); + + +static void aux_lines (lua_State *L, int idx, int toclose) { + lua_pushvalue(L, idx); + lua_pushboolean(L, toclose); /* close/not close file when finished */ + lua_pushcclosure(L, io_readline, 2); +} + + +static int f_lines (lua_State *L) { + tofile(L); /* check that it's a valid file handle */ + aux_lines(L, 1, 0); + return 1; +} + + +static int io_lines (lua_State *L) { + if (lua_isnoneornil(L, 1)) { /* no arguments? */ + /* will iterate over default input */ + lua_rawgeti(L, LUA_ENVIRONINDEX, IO_INPUT); + return f_lines(L); + } + else { + const char *filename = luaL_checkstring(L, 1); + FILE **pf = newfile(L); + *pf = fopen(filename, "r"); + if (*pf == NULL) + fileerror(L, 1, filename); + aux_lines(L, lua_gettop(L), 1); + return 1; + } +} + + +/* +** {====================================================== +** READ +** ======================================================= +*/ + + +static int read_number (lua_State *L, FILE *f) { + lua_Number d; + if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) { + lua_pushnumber(L, d); + return 1; + } + else { + lua_pushnil(L); /* "result" to be removed */ + return 0; /* read fails */ + } +} + + +static int test_eof (lua_State *L, FILE *f) { + int c = getc(f); + ungetc(c, f); + lua_pushlstring(L, NULL, 0); + return (c != EOF); +} + + +static int read_line (lua_State *L, FILE *f) { + luaL_Buffer b; + luaL_buffinit(L, &b); + for (;;) { + size_t l; + char *p = luaL_prepbuffer(&b); + if (fgets(p, LUAL_BUFFERSIZE, f) == NULL) { /* eof? */ + luaL_pushresult(&b); /* close buffer */ + return (lua_objlen(L, -1) > 0); /* check whether read something */ + } + l = strlen(p); + if (l == 0 || p[l-1] != '\n') + luaL_addsize(&b, l); + else { + luaL_addsize(&b, l - 1); /* do not include `eol' */ + luaL_pushresult(&b); /* close buffer */ + return 1; /* read at least an `eol' */ + } + } +} + + +static int read_chars (lua_State *L, FILE *f, size_t n) { + size_t rlen; /* how much to read */ + size_t nr; /* number of chars actually read */ + luaL_Buffer b; + luaL_buffinit(L, &b); + rlen = LUAL_BUFFERSIZE; /* try to read that much each time */ + do { + char *p = luaL_prepbuffer(&b); + if (rlen > n) rlen = n; /* cannot read more than asked */ + nr = fread(p, sizeof(char), rlen, f); + luaL_addsize(&b, nr); + n -= nr; /* still have to read `n' chars */ + } while (n > 0 && nr == rlen); /* until end of count or eof */ + luaL_pushresult(&b); /* close buffer */ + return (n == 0 || lua_objlen(L, -1) > 0); +} + + +static int g_read (lua_State *L, FILE *f, int first) { + int nargs = lua_gettop(L) - 1; + int success; + int n; + clearerr(f); + if (nargs == 0) { /* no arguments? */ + success = read_line(L, f); + n = first+1; /* to return 1 result */ + } + else { /* ensure stack space for all results and for auxlib's buffer */ + luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments"); + success = 1; + for (n = first; nargs-- && success; n++) { + if (lua_type(L, n) == LUA_TNUMBER) { + size_t l = (size_t)lua_tointeger(L, n); + success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l); + } + else { + const char *p = lua_tostring(L, n); + luaL_argcheck(L, p && p[0] == '*', n, "invalid option"); + switch (p[1]) { + case 'n': /* number */ + success = read_number(L, f); + break; + case 'l': /* line */ + success = read_line(L, f); + break; + case 'a': /* file */ + read_chars(L, f, ~((size_t)0)); /* read MAX_SIZE_T chars */ + success = 1; /* always success */ + break; + default: + return luaL_argerror(L, n, "invalid format"); + } + } + } + } + if (ferror(f)) + return pushresult(L, 0, NULL); + if (!success) { + lua_pop(L, 1); /* remove last result */ + lua_pushnil(L); /* push nil instead */ + } + return n - first; +} + + +static int io_read (lua_State *L) { + return g_read(L, getiofile(L, IO_INPUT), 1); +} + + +static int f_read (lua_State *L) { + return g_read(L, tofile(L), 2); +} + + +static int io_readline (lua_State *L) { + FILE *f = *(FILE **)lua_touserdata(L, lua_upvalueindex(1)); + int sucess; + if (f == NULL) /* file is already closed? */ + luaL_error(L, "file is already closed"); + sucess = read_line(L, f); + if (ferror(f)) + return luaL_error(L, "%s", strerror(errno)); + if (sucess) return 1; + else { /* EOF */ + if (lua_toboolean(L, lua_upvalueindex(2))) { /* generator created file? */ + lua_settop(L, 0); + lua_pushvalue(L, lua_upvalueindex(1)); + aux_close(L); /* close it */ + } + return 0; + } +} + +/* }====================================================== */ + + +static int g_write (lua_State *L, FILE *f, int arg) { + int nargs = lua_gettop(L) - 1; + int status = 1; + for (; nargs--; arg++) { + if (lua_type(L, arg) == LUA_TNUMBER) { + /* optimization: could be done exactly as for strings */ + status = status && + fprintf(f, LUA_NUMBER_FMT, lua_tonumber(L, arg)) > 0; + } + else { + size_t l; + const char *s = luaL_checklstring(L, arg, &l); + status = status && (fwrite(s, sizeof(char), l, f) == l); + } + } + return pushresult(L, status, NULL); +} + + +static int io_write (lua_State *L) { + return g_write(L, getiofile(L, IO_OUTPUT), 1); +} + + +static int f_write (lua_State *L) { + return g_write(L, tofile(L), 2); +} + + +static int f_seek (lua_State *L) { + static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END}; + static const char *const modenames[] = {"set", "cur", "end", NULL}; + FILE *f = tofile(L); + int op = luaL_checkoption(L, 2, "cur", modenames); + long offset = luaL_optlong(L, 3, 0); + op = fseek(f, offset, mode[op]); + if (op) + return pushresult(L, 0, NULL); /* error */ + else { + lua_pushinteger(L, ftell(f)); + return 1; + } +} + + +static int f_setvbuf (lua_State *L) { + static const int mode[] = {_IONBF, _IOFBF, _IOLBF}; + static const char *const modenames[] = {"no", "full", "line", NULL}; + FILE *f = tofile(L); + int op = luaL_checkoption(L, 2, NULL, modenames); + lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE); + int res = setvbuf(f, NULL, mode[op], sz); + return pushresult(L, res == 0, NULL); +} + + + +static int io_flush (lua_State *L) { + return pushresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL); +} + + +static int f_flush (lua_State *L) { + return pushresult(L, fflush(tofile(L)) == 0, NULL); +} + + +static const luaL_Reg iolib[] = { + {"close", io_close}, + {"flush", io_flush}, + {"input", io_input}, + {"lines", io_lines}, + {"open", io_open}, + {"output", io_output}, + {"popen", io_popen}, + {"read", io_read}, + {"tmpfile", io_tmpfile}, + {"type", io_type}, + {"write", io_write}, + {NULL, NULL} +}; + + +static const luaL_Reg flib[] = { + {"close", io_close}, + {"flush", f_flush}, + {"lines", f_lines}, + {"read", f_read}, + {"seek", f_seek}, + {"setvbuf", f_setvbuf}, + {"write", f_write}, + {"__gc", io_gc}, + {"__tostring", io_tostring}, + {NULL, NULL} +}; + + +static void createmeta (lua_State *L) { + luaL_newmetatable(L, LUA_FILEHANDLE); /* create metatable for file handles */ + lua_pushvalue(L, -1); /* push metatable */ + lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */ + luaL_register(L, NULL, flib); /* file methods */ +} + + +static void createstdfile (lua_State *L, FILE *f, int k, const char *fname) { + *newfile(L) = f; + if (k > 0) { + lua_pushvalue(L, -1); + lua_rawseti(L, LUA_ENVIRONINDEX, k); + } + lua_pushvalue(L, -2); /* copy environment */ + lua_setfenv(L, -2); /* set it */ + lua_setfield(L, -3, fname); +} + + +static void newfenv (lua_State *L, lua_CFunction cls) { + lua_createtable(L, 0, 1); + lua_pushcfunction(L, cls); + lua_setfield(L, -2, "__close"); +} + + +LUALIB_API int luaopen_io (lua_State *L) { + createmeta(L); + /* create (private) environment (with fields IO_INPUT, IO_OUTPUT, __close) */ + newfenv(L, io_fclose); + lua_replace(L, LUA_ENVIRONINDEX); + /* open library */ + luaL_register(L, LUA_IOLIBNAME, iolib); + /* create (and set) default files */ + newfenv(L, io_noclose); /* close function for default files */ + createstdfile(L, stdin, IO_INPUT, "stdin"); + createstdfile(L, stdout, IO_OUTPUT, "stdout"); + createstdfile(L, stderr, 0, "stderr"); + lua_pop(L, 1); /* pop environment for default files */ + lua_getfield(L, -1, "popen"); + newfenv(L, io_pclose); /* create environment for 'popen' */ + lua_setfenv(L, -2); /* set fenv for 'popen' */ + lua_pop(L, 1); /* pop 'popen' */ + return 1; +} + diff --git a/lib/lua/llex.c b/lib/lua/llex.c new file mode 100644 index 000000000000..88c6790c076a --- /dev/null +++ b/lib/lua/llex.c @@ -0,0 +1,463 @@ +/* +** $Id: llex.c,v 2.20.1.2 2009/11/23 14:58:22 roberto Exp $ +** Lexical Analyzer +** See Copyright Notice in lua.h +*/ + + +#include +#include +#include + +#define llex_c +#define LUA_CORE + +#include "lua.h" + +#include "ldo.h" +#include "llex.h" +#include "lobject.h" +#include "lparser.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "lzio.h" + + + +#define next(ls) (ls->current = zgetc(ls->z)) + + + + +#define currIsNewline(ls) (ls->current == '\n' || ls->current == '\r') + + +/* ORDER RESERVED */ +const char *const luaX_tokens [] = { + "and", "break", "do", "else", "elseif", + "end", "false", "for", "function", "if", + "in", "local", "nil", "not", "or", "repeat", + "return", "then", "true", "until", "while", + "..", "...", "==", ">=", "<=", "~=", + "", "", "", "", + NULL +}; + + +#define save_and_next(ls) (save(ls, ls->current), next(ls)) + + +static void save (LexState *ls, int c) { + Mbuffer *b = ls->buff; + if (b->n + 1 > b->buffsize) { + size_t newsize; + if (b->buffsize >= MAX_SIZET/2) + luaX_lexerror(ls, "lexical element too long", 0); + newsize = b->buffsize * 2; + luaZ_resizebuffer(ls->L, b, newsize); + } + b->buffer[b->n++] = cast(char, c); +} + + +void luaX_init (lua_State *L) { + int i; + for (i=0; itsv.reserved = cast_byte(i+1); /* reserved word */ + } +} + + +#define MAXSRC 80 + + +const char *luaX_token2str (LexState *ls, int token) { + if (token < FIRST_RESERVED) { + lua_assert(token == cast(unsigned char, token)); + return (iscntrl(token)) ? luaO_pushfstring(ls->L, "char(%d)", token) : + luaO_pushfstring(ls->L, "%c", token); + } + else + return luaX_tokens[token-FIRST_RESERVED]; +} + + +static const char *txtToken (LexState *ls, int token) { + switch (token) { + case TK_NAME: + case TK_STRING: + case TK_NUMBER: + save(ls, '\0'); + return luaZ_buffer(ls->buff); + default: + return luaX_token2str(ls, token); + } +} + + +void luaX_lexerror (LexState *ls, const char *msg, int token) { + char buff[MAXSRC]; + luaO_chunkid(buff, getstr(ls->source), MAXSRC); + msg = luaO_pushfstring(ls->L, "%s:%d: %s", buff, ls->linenumber, msg); + if (token) + luaO_pushfstring(ls->L, "%s near " LUA_QS, msg, txtToken(ls, token)); + luaD_throw(ls->L, LUA_ERRSYNTAX); +} + + +void luaX_syntaxerror (LexState *ls, const char *msg) { + luaX_lexerror(ls, msg, ls->t.token); +} + + +TString *luaX_newstring (LexState *ls, const char *str, size_t l) { + lua_State *L = ls->L; + TString *ts = luaS_newlstr(L, str, l); + TValue *o = luaH_setstr(L, ls->fs->h, ts); /* entry for `str' */ + if (ttisnil(o)) { + setbvalue(o, 1); /* make sure `str' will not be collected */ + luaC_checkGC(L); + } + return ts; +} + + +static void inclinenumber (LexState *ls) { + int old = ls->current; + lua_assert(currIsNewline(ls)); + next(ls); /* skip `\n' or `\r' */ + if (currIsNewline(ls) && ls->current != old) + next(ls); /* skip `\n\r' or `\r\n' */ + if (++ls->linenumber >= MAX_INT) + luaX_syntaxerror(ls, "chunk has too many lines"); +} + + +void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source) { + ls->decpoint = '.'; + ls->L = L; + ls->lookahead.token = TK_EOS; /* no look-ahead token */ + ls->z = z; + ls->fs = NULL; + ls->linenumber = 1; + ls->lastline = 1; + ls->source = source; + luaZ_resizebuffer(ls->L, ls->buff, LUA_MINBUFFER); /* initialize buffer */ + next(ls); /* read first char */ +} + + + +/* +** ======================================================= +** LEXICAL ANALYZER +** ======================================================= +*/ + + + +static int check_next (LexState *ls, const char *set) { + if (!strchr(set, ls->current)) + return 0; + save_and_next(ls); + return 1; +} + + +static void buffreplace (LexState *ls, char from, char to) { + size_t n = luaZ_bufflen(ls->buff); + char *p = luaZ_buffer(ls->buff); + while (n--) + if (p[n] == from) p[n] = to; +} + + +static void trydecpoint (LexState *ls, SemInfo *seminfo) { + /* format error: try to update decimal point separator */ + struct lconv *cv = localeconv(); + char old = ls->decpoint; + ls->decpoint = (cv ? cv->decimal_point[0] : '.'); + buffreplace(ls, old, ls->decpoint); /* try updated decimal separator */ + if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r)) { + /* format error with correct decimal point: no more options */ + buffreplace(ls, ls->decpoint, '.'); /* undo change (for error message) */ + luaX_lexerror(ls, "malformed number", TK_NUMBER); + } +} + + +/* LUA_NUMBER */ +static void read_numeral (LexState *ls, SemInfo *seminfo) { + lua_assert(isdigit(ls->current)); + do { + save_and_next(ls); + } while (isdigit(ls->current) || ls->current == '.'); + if (check_next(ls, "Ee")) /* `E'? */ + check_next(ls, "+-"); /* optional exponent sign */ + while (isalnum(ls->current) || ls->current == '_') + save_and_next(ls); + save(ls, '\0'); + buffreplace(ls, '.', ls->decpoint); /* follow locale for decimal point */ + if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r)) /* format error? */ + trydecpoint(ls, seminfo); /* try to update decimal point separator */ +} + + +static int skip_sep (LexState *ls) { + int count = 0; + int s = ls->current; + lua_assert(s == '[' || s == ']'); + save_and_next(ls); + while (ls->current == '=') { + save_and_next(ls); + count++; + } + return (ls->current == s) ? count : (-count) - 1; +} + + +static void read_long_string (LexState *ls, SemInfo *seminfo, int sep) { + int cont = 0; + (void)(cont); /* avoid warnings when `cont' is not used */ + save_and_next(ls); /* skip 2nd `[' */ + if (currIsNewline(ls)) /* string starts with a newline? */ + inclinenumber(ls); /* skip it */ + for (;;) { + switch (ls->current) { + case EOZ: + luaX_lexerror(ls, (seminfo) ? "unfinished long string" : + "unfinished long comment", TK_EOS); + break; /* to avoid warnings */ +#if defined(LUA_COMPAT_LSTR) + case '[': { + if (skip_sep(ls) == sep) { + save_and_next(ls); /* skip 2nd `[' */ + cont++; +#if LUA_COMPAT_LSTR == 1 + if (sep == 0) + luaX_lexerror(ls, "nesting of [[...]] is deprecated", '['); +#endif + } + break; + } +#endif + case ']': { + if (skip_sep(ls) == sep) { + save_and_next(ls); /* skip 2nd `]' */ +#if defined(LUA_COMPAT_LSTR) && LUA_COMPAT_LSTR == 2 + cont--; + if (sep == 0 && cont >= 0) break; +#endif + goto endloop; + } + break; + } + case '\n': + case '\r': { + save(ls, '\n'); + inclinenumber(ls); + if (!seminfo) luaZ_resetbuffer(ls->buff); /* avoid wasting space */ + break; + } + default: { + if (seminfo) save_and_next(ls); + else next(ls); + } + } + } endloop: + if (seminfo) + seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + (2 + sep), + luaZ_bufflen(ls->buff) - 2*(2 + sep)); +} + + +static void read_string (LexState *ls, int del, SemInfo *seminfo) { + save_and_next(ls); + while (ls->current != del) { + switch (ls->current) { + case EOZ: + luaX_lexerror(ls, "unfinished string", TK_EOS); + continue; /* to avoid warnings */ + case '\n': + case '\r': + luaX_lexerror(ls, "unfinished string", TK_STRING); + continue; /* to avoid warnings */ + case '\\': { + int c; + next(ls); /* do not save the `\' */ + switch (ls->current) { + case 'a': c = '\a'; break; + case 'b': c = '\b'; break; + case 'f': c = '\f'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case 'v': c = '\v'; break; + case '\n': /* go through */ + case '\r': save(ls, '\n'); inclinenumber(ls); continue; + case EOZ: continue; /* will raise an error next loop */ + default: { + if (!isdigit(ls->current)) + save_and_next(ls); /* handles \\, \", \', and \? */ + else { /* \xxx */ + int i = 0; + c = 0; + do { + c = 10*c + (ls->current-'0'); + next(ls); + } while (++i<3 && isdigit(ls->current)); + if (c > UCHAR_MAX) + luaX_lexerror(ls, "escape sequence too large", TK_STRING); + save(ls, c); + } + continue; + } + } + save(ls, c); + next(ls); + continue; + } + default: + save_and_next(ls); + } + } + save_and_next(ls); /* skip delimiter */ + seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + 1, + luaZ_bufflen(ls->buff) - 2); +} + + +static int llex (LexState *ls, SemInfo *seminfo) { + luaZ_resetbuffer(ls->buff); + for (;;) { + switch (ls->current) { + case '\n': + case '\r': { + inclinenumber(ls); + continue; + } + case '-': { + next(ls); + if (ls->current != '-') return '-'; + /* else is a comment */ + next(ls); + if (ls->current == '[') { + int sep = skip_sep(ls); + luaZ_resetbuffer(ls->buff); /* `skip_sep' may dirty the buffer */ + if (sep >= 0) { + read_long_string(ls, NULL, sep); /* long comment */ + luaZ_resetbuffer(ls->buff); + continue; + } + } + /* else short comment */ + while (!currIsNewline(ls) && ls->current != EOZ) + next(ls); + continue; + } + case '[': { + int sep = skip_sep(ls); + if (sep >= 0) { + read_long_string(ls, seminfo, sep); + return TK_STRING; + } + else if (sep == -1) return '['; + else luaX_lexerror(ls, "invalid long string delimiter", TK_STRING); + } + case '=': { + next(ls); + if (ls->current != '=') return '='; + else { next(ls); return TK_EQ; } + } + case '<': { + next(ls); + if (ls->current != '=') return '<'; + else { next(ls); return TK_LE; } + } + case '>': { + next(ls); + if (ls->current != '=') return '>'; + else { next(ls); return TK_GE; } + } + case '~': { + next(ls); + if (ls->current != '=') return '~'; + else { next(ls); return TK_NE; } + } + case '"': + case '\'': { + read_string(ls, ls->current, seminfo); + return TK_STRING; + } + case '.': { + save_and_next(ls); + if (check_next(ls, ".")) { + if (check_next(ls, ".")) + return TK_DOTS; /* ... */ + else return TK_CONCAT; /* .. */ + } + else if (!isdigit(ls->current)) return '.'; + else { + read_numeral(ls, seminfo); + return TK_NUMBER; + } + } + case EOZ: { + return TK_EOS; + } + default: { + if (isspace(ls->current)) { + lua_assert(!currIsNewline(ls)); + next(ls); + continue; + } + else if (isdigit(ls->current)) { + read_numeral(ls, seminfo); + return TK_NUMBER; + } + else if (isalpha(ls->current) || ls->current == '_') { + /* identifier or reserved word */ + TString *ts; + do { + save_and_next(ls); + } while (isalnum(ls->current) || ls->current == '_'); + ts = luaX_newstring(ls, luaZ_buffer(ls->buff), + luaZ_bufflen(ls->buff)); + if (ts->tsv.reserved > 0) /* reserved word? */ + return ts->tsv.reserved - 1 + FIRST_RESERVED; + else { + seminfo->ts = ts; + return TK_NAME; + } + } + else { + int c = ls->current; + next(ls); + return c; /* single-char tokens (+ - / ...) */ + } + } + } + } +} + + +void luaX_next (LexState *ls) { + ls->lastline = ls->linenumber; + if (ls->lookahead.token != TK_EOS) { /* is there a look-ahead token? */ + ls->t = ls->lookahead; /* use this one */ + ls->lookahead.token = TK_EOS; /* and discharge it */ + } + else + ls->t.token = llex(ls, &ls->t.seminfo); /* read next token */ +} + + +void luaX_lookahead (LexState *ls) { + lua_assert(ls->lookahead.token == TK_EOS); + ls->lookahead.token = llex(ls, &ls->lookahead.seminfo); +} + diff --git a/lib/lua/llex.h b/lib/lua/llex.h new file mode 100644 index 000000000000..a9201cee4847 --- /dev/null +++ b/lib/lua/llex.h @@ -0,0 +1,81 @@ +/* +** $Id: llex.h,v 1.58.1.1 2007/12/27 13:02:25 roberto Exp $ +** Lexical Analyzer +** See Copyright Notice in lua.h +*/ + +#ifndef llex_h +#define llex_h + +#include "lobject.h" +#include "lzio.h" + + +#define FIRST_RESERVED 257 + +/* maximum length of a reserved word */ +#define TOKEN_LEN (sizeof("function")/sizeof(char)) + + +/* +* WARNING: if you change the order of this enumeration, +* grep "ORDER RESERVED" +*/ +enum RESERVED { + /* terminal symbols denoted by reserved words */ + TK_AND = FIRST_RESERVED, TK_BREAK, + TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION, + TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT, + TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE, + /* other terminal symbols */ + TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_NUMBER, + TK_NAME, TK_STRING, TK_EOS +}; + +/* number of reserved words */ +#define NUM_RESERVED (cast(int, TK_WHILE-FIRST_RESERVED+1)) + + +/* array with token `names' */ +LUAI_DATA const char *const luaX_tokens []; + + +typedef union { + lua_Number r; + TString *ts; +} SemInfo; /* semantics information */ + + +typedef struct Token { + int token; + SemInfo seminfo; +} Token; + + +typedef struct LexState { + int current; /* current character (charint) */ + int linenumber; /* input line counter */ + int lastline; /* line of last token `consumed' */ + Token t; /* current token */ + Token lookahead; /* look ahead token */ + struct FuncState *fs; /* `FuncState' is private to the parser */ + struct lua_State *L; + ZIO *z; /* input stream */ + Mbuffer *buff; /* buffer for tokens */ + TString *source; /* current source name */ + char decpoint; /* locale decimal point */ +} LexState; + + +LUAI_FUNC void luaX_init (lua_State *L); +LUAI_FUNC void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, + TString *source); +LUAI_FUNC TString *luaX_newstring (LexState *ls, const char *str, size_t l); +LUAI_FUNC void luaX_next (LexState *ls); +LUAI_FUNC void luaX_lookahead (LexState *ls); +LUAI_FUNC void luaX_lexerror (LexState *ls, const char *msg, int token); +LUAI_FUNC void luaX_syntaxerror (LexState *ls, const char *s); +LUAI_FUNC const char *luaX_token2str (LexState *ls, int token); + + +#endif diff --git a/lib/lua/llimits.h b/lib/lua/llimits.h new file mode 100644 index 000000000000..ca8dcb72244b --- /dev/null +++ b/lib/lua/llimits.h @@ -0,0 +1,128 @@ +/* +** $Id: llimits.h,v 1.69.1.1 2007/12/27 13:02:25 roberto Exp $ +** Limits, basic types, and some other `installation-dependent' definitions +** See Copyright Notice in lua.h +*/ + +#ifndef llimits_h +#define llimits_h + + +#include +#include + + +#include "lua.h" + + +typedef LUAI_UINT32 lu_int32; + +typedef LUAI_UMEM lu_mem; + +typedef LUAI_MEM l_mem; + + + +/* chars used as small naturals (so that `char' is reserved for characters) */ +typedef unsigned char lu_byte; + + +#define MAX_SIZET ((size_t)(~(size_t)0)-2) + +#define MAX_LUMEM ((lu_mem)(~(lu_mem)0)-2) + + +#define MAX_INT (INT_MAX-2) /* maximum value of an int (-2 for safety) */ + +/* +** conversion of pointer to integer +** this is for hashing only; there is no problem if the integer +** cannot hold the whole pointer value +*/ +#define IntPoint(p) ((unsigned int)(lu_mem)(p)) + + + +/* type to ensure maximum alignment */ +typedef LUAI_USER_ALIGNMENT_T L_Umaxalign; + + +/* result of a `usual argument conversion' over lua_Number */ +typedef LUAI_UACNUMBER l_uacNumber; + + +/* internal assertions for in-house debugging */ +#ifdef lua_assert + +#define check_exp(c,e) (lua_assert(c), (e)) +#define api_check(l,e) lua_assert(e) + +#else + +#define lua_assert(c) ((void)0) +#define check_exp(c,e) (e) +#define api_check luai_apicheck + +#endif + + +#ifndef UNUSED +#define UNUSED(x) ((void)(x)) /* to avoid warnings */ +#endif + + +#ifndef cast +#define cast(t, exp) ((t)(exp)) +#endif + +#define cast_byte(i) cast(lu_byte, (i)) +#define cast_num(i) cast(lua_Number, (i)) +#define cast_int(i) cast(int, (i)) + + + +/* +** type for virtual-machine instructions +** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h) +*/ +typedef lu_int32 Instruction; + + + +/* maximum stack for a Lua function */ +#define MAXSTACK 250 + + + +/* minimum size for the string table (must be power of 2) */ +#ifndef MINSTRTABSIZE +#define MINSTRTABSIZE 32 +#endif + + +/* minimum size for string buffer */ +#ifndef LUA_MINBUFFER +#define LUA_MINBUFFER 32 +#endif + + +#ifndef lua_lock +#define lua_lock(L) ((void) 0) +#define lua_unlock(L) ((void) 0) +#endif + +#ifndef luai_threadyield +#define luai_threadyield(L) {lua_unlock(L); lua_lock(L);} +#endif + + +/* +** macro to control inclusion of some hard tests on stack reallocation +*/ +#ifndef HARDSTACKTESTS +#define condhardstacktests(x) ((void)0) +#else +#define condhardstacktests(x) x +#endif + +#endif diff --git a/lib/lua/lmathlib.c b/lib/lua/lmathlib.c new file mode 100644 index 000000000000..441fbf736c2b --- /dev/null +++ b/lib/lua/lmathlib.c @@ -0,0 +1,263 @@ +/* +** $Id: lmathlib.c,v 1.67.1.1 2007/12/27 13:02:25 roberto Exp $ +** Standard mathematical library +** See Copyright Notice in lua.h +*/ + + +#include +#include + +#define lmathlib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +#undef PI +#define PI (3.14159265358979323846) +#define RADIANS_PER_DEGREE (PI/180.0) + + + +static int math_abs (lua_State *L) { + lua_pushnumber(L, fabs(luaL_checknumber(L, 1))); + return 1; +} + +static int math_sin (lua_State *L) { + lua_pushnumber(L, sin(luaL_checknumber(L, 1))); + return 1; +} + +static int math_sinh (lua_State *L) { + lua_pushnumber(L, sinh(luaL_checknumber(L, 1))); + return 1; +} + +static int math_cos (lua_State *L) { + lua_pushnumber(L, cos(luaL_checknumber(L, 1))); + return 1; +} + +static int math_cosh (lua_State *L) { + lua_pushnumber(L, cosh(luaL_checknumber(L, 1))); + return 1; +} + +static int math_tan (lua_State *L) { + lua_pushnumber(L, tan(luaL_checknumber(L, 1))); + return 1; +} + +static int math_tanh (lua_State *L) { + lua_pushnumber(L, tanh(luaL_checknumber(L, 1))); + return 1; +} + +static int math_asin (lua_State *L) { + lua_pushnumber(L, asin(luaL_checknumber(L, 1))); + return 1; +} + +static int math_acos (lua_State *L) { + lua_pushnumber(L, acos(luaL_checknumber(L, 1))); + return 1; +} + +static int math_atan (lua_State *L) { + lua_pushnumber(L, atan(luaL_checknumber(L, 1))); + return 1; +} + +static int math_atan2 (lua_State *L) { + lua_pushnumber(L, atan2(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); + return 1; +} + +static int math_ceil (lua_State *L) { + lua_pushnumber(L, ceil(luaL_checknumber(L, 1))); + return 1; +} + +static int math_floor (lua_State *L) { + lua_pushnumber(L, floor(luaL_checknumber(L, 1))); + return 1; +} + +static int math_fmod (lua_State *L) { + lua_pushnumber(L, fmod(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); + return 1; +} + +static int math_modf (lua_State *L) { + double ip; + double fp = modf(luaL_checknumber(L, 1), &ip); + lua_pushnumber(L, ip); + lua_pushnumber(L, fp); + return 2; +} + +static int math_sqrt (lua_State *L) { + lua_pushnumber(L, sqrt(luaL_checknumber(L, 1))); + return 1; +} + +static int math_pow (lua_State *L) { + lua_pushnumber(L, pow(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); + return 1; +} + +static int math_log (lua_State *L) { + lua_pushnumber(L, log(luaL_checknumber(L, 1))); + return 1; +} + +static int math_log10 (lua_State *L) { + lua_pushnumber(L, log10(luaL_checknumber(L, 1))); + return 1; +} + +static int math_exp (lua_State *L) { + lua_pushnumber(L, exp(luaL_checknumber(L, 1))); + return 1; +} + +static int math_deg (lua_State *L) { + lua_pushnumber(L, luaL_checknumber(L, 1)/RADIANS_PER_DEGREE); + return 1; +} + +static int math_rad (lua_State *L) { + lua_pushnumber(L, luaL_checknumber(L, 1)*RADIANS_PER_DEGREE); + return 1; +} + +static int math_frexp (lua_State *L) { + int e; + lua_pushnumber(L, frexp(luaL_checknumber(L, 1), &e)); + lua_pushinteger(L, e); + return 2; +} + +static int math_ldexp (lua_State *L) { + lua_pushnumber(L, ldexp(luaL_checknumber(L, 1), luaL_checkint(L, 2))); + return 1; +} + + + +static int math_min (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + lua_Number dmin = luaL_checknumber(L, 1); + int i; + for (i=2; i<=n; i++) { + lua_Number d = luaL_checknumber(L, i); + if (d < dmin) + dmin = d; + } + lua_pushnumber(L, dmin); + return 1; +} + + +static int math_max (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + lua_Number dmax = luaL_checknumber(L, 1); + int i; + for (i=2; i<=n; i++) { + lua_Number d = luaL_checknumber(L, i); + if (d > dmax) + dmax = d; + } + lua_pushnumber(L, dmax); + return 1; +} + + +static int math_random (lua_State *L) { + /* the `%' avoids the (rare) case of r==1, and is needed also because on + some systems (SunOS!) `rand()' may return a value larger than RAND_MAX */ + lua_Number r = (lua_Number)(rand()%RAND_MAX) / (lua_Number)RAND_MAX; + switch (lua_gettop(L)) { /* check number of arguments */ + case 0: { /* no arguments */ + lua_pushnumber(L, r); /* Number between 0 and 1 */ + break; + } + case 1: { /* only upper limit */ + int u = luaL_checkint(L, 1); + luaL_argcheck(L, 1<=u, 1, "interval is empty"); + lua_pushnumber(L, floor(r*u)+1); /* int between 1 and `u' */ + break; + } + case 2: { /* lower and upper limits */ + int l = luaL_checkint(L, 1); + int u = luaL_checkint(L, 2); + luaL_argcheck(L, l<=u, 2, "interval is empty"); + lua_pushnumber(L, floor(r*(u-l+1))+l); /* int between `l' and `u' */ + break; + } + default: return luaL_error(L, "wrong number of arguments"); + } + return 1; +} + + +static int math_randomseed (lua_State *L) { + srand(luaL_checkint(L, 1)); + return 0; +} + + +static const luaL_Reg mathlib[] = { + {"abs", math_abs}, + {"acos", math_acos}, + {"asin", math_asin}, + {"atan2", math_atan2}, + {"atan", math_atan}, + {"ceil", math_ceil}, + {"cosh", math_cosh}, + {"cos", math_cos}, + {"deg", math_deg}, + {"exp", math_exp}, + {"floor", math_floor}, + {"fmod", math_fmod}, + {"frexp", math_frexp}, + {"ldexp", math_ldexp}, + {"log10", math_log10}, + {"log", math_log}, + {"max", math_max}, + {"min", math_min}, + {"modf", math_modf}, + {"pow", math_pow}, + {"rad", math_rad}, + {"random", math_random}, + {"randomseed", math_randomseed}, + {"sinh", math_sinh}, + {"sin", math_sin}, + {"sqrt", math_sqrt}, + {"tanh", math_tanh}, + {"tan", math_tan}, + {NULL, NULL} +}; + + +/* +** Open math library +*/ +LUALIB_API int luaopen_math (lua_State *L) { + luaL_register(L, LUA_MATHLIBNAME, mathlib); + lua_pushnumber(L, PI); + lua_setfield(L, -2, "pi"); + lua_pushnumber(L, HUGE_VAL); + lua_setfield(L, -2, "huge"); +#if defined(LUA_COMPAT_MOD) + lua_getfield(L, -1, "fmod"); + lua_setfield(L, -2, "mod"); +#endif + return 1; +} + diff --git a/lib/lua/lmem.c b/lib/lua/lmem.c new file mode 100644 index 000000000000..ae7d8c965f6e --- /dev/null +++ b/lib/lua/lmem.c @@ -0,0 +1,86 @@ +/* +** $Id: lmem.c,v 1.70.1.1 2007/12/27 13:02:25 roberto Exp $ +** Interface to Memory Manager +** See Copyright Notice in lua.h +*/ + + +#include + +#define lmem_c +#define LUA_CORE + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" + + + +/* +** About the realloc function: +** void * frealloc (void *ud, void *ptr, size_t osize, size_t nsize); +** (`osize' is the old size, `nsize' is the new size) +** +** Lua ensures that (ptr == NULL) iff (osize == 0). +** +** * frealloc(ud, NULL, 0, x) creates a new block of size `x' +** +** * frealloc(ud, p, x, 0) frees the block `p' +** (in this specific case, frealloc must return NULL). +** particularly, frealloc(ud, NULL, 0, 0) does nothing +** (which is equivalent to free(NULL) in ANSI C) +** +** frealloc returns NULL if it cannot create or reallocate the area +** (any reallocation to an equal or smaller size cannot fail!) +*/ + + + +#define MINSIZEARRAY 4 + + +void *luaM_growaux_ (lua_State *L, void *block, int *size, size_t size_elems, + int limit, const char *errormsg) { + void *newblock; + int newsize; + if (*size >= limit/2) { /* cannot double it? */ + if (*size >= limit) /* cannot grow even a little? */ + luaG_runerror(L, errormsg); + newsize = limit; /* still have at least one free place */ + } + else { + newsize = (*size)*2; + if (newsize < MINSIZEARRAY) + newsize = MINSIZEARRAY; /* minimum size */ + } + newblock = luaM_reallocv(L, block, *size, newsize, size_elems); + *size = newsize; /* update only when everything else is OK */ + return newblock; +} + + +void *luaM_toobig (lua_State *L) { + luaG_runerror(L, "memory allocation error: block too big"); + return NULL; /* to avoid warnings */ +} + + + +/* +** generic allocation routine. +*/ +void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) { + global_State *g = G(L); + lua_assert((osize == 0) == (block == NULL)); + block = (*g->frealloc)(g->ud, block, osize, nsize); + if (block == NULL && nsize > 0) + luaD_throw(L, LUA_ERRMEM); + lua_assert((nsize == 0) == (block == NULL)); + g->totalbytes = (g->totalbytes - osize) + nsize; + return block; +} + diff --git a/lib/lua/lmem.h b/lib/lua/lmem.h new file mode 100644 index 000000000000..7c2dcb32207a --- /dev/null +++ b/lib/lua/lmem.h @@ -0,0 +1,49 @@ +/* +** $Id: lmem.h,v 1.31.1.1 2007/12/27 13:02:25 roberto Exp $ +** Interface to Memory Manager +** See Copyright Notice in lua.h +*/ + +#ifndef lmem_h +#define lmem_h + + +#include + +#include "llimits.h" +#include "lua.h" + +#define MEMERRMSG "not enough memory" + + +#define luaM_reallocv(L,b,on,n,e) \ + ((cast(size_t, (n)+1) <= MAX_SIZET/(e)) ? /* +1 to avoid warnings */ \ + luaM_realloc_(L, (b), (on)*(e), (n)*(e)) : \ + luaM_toobig(L)) + +#define luaM_freemem(L, b, s) luaM_realloc_(L, (b), (s), 0) +#define luaM_free(L, b) luaM_realloc_(L, (b), sizeof(*(b)), 0) +#define luaM_freearray(L, b, n, t) luaM_reallocv(L, (b), n, 0, sizeof(t)) + +#define luaM_malloc(L,t) luaM_realloc_(L, NULL, 0, (t)) +#define luaM_new(L,t) cast(t *, luaM_malloc(L, sizeof(t))) +#define luaM_newvector(L,n,t) \ + cast(t *, luaM_reallocv(L, NULL, 0, n, sizeof(t))) + +#define luaM_growvector(L,v,nelems,size,t,limit,e) \ + if ((nelems)+1 > (size)) \ + ((v)=cast(t *, luaM_growaux_(L,v,&(size),sizeof(t),limit,e))) + +#define luaM_reallocvector(L, v,oldn,n,t) \ + ((v)=cast(t *, luaM_reallocv(L, v, oldn, n, sizeof(t)))) + + +LUAI_FUNC void *luaM_realloc_ (lua_State *L, void *block, size_t oldsize, + size_t size); +LUAI_FUNC void *luaM_toobig (lua_State *L); +LUAI_FUNC void *luaM_growaux_ (lua_State *L, void *block, int *size, + size_t size_elem, int limit, + const char *errormsg); + +#endif + diff --git a/lib/lua/loadlib.c b/lib/lua/loadlib.c new file mode 100644 index 000000000000..6158c5353df2 --- /dev/null +++ b/lib/lua/loadlib.c @@ -0,0 +1,666 @@ +/* +** $Id: loadlib.c,v 1.52.1.4 2009/09/09 13:17:16 roberto Exp $ +** Dynamic library loader for Lua +** See Copyright Notice in lua.h +** +** This module contains an implementation of loadlib for Unix systems +** that have dlfcn, an implementation for Darwin (Mac OS X), an +** implementation for Windows, and a stub for other systems. +*/ + + +#include +#include + + +#define loadlib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +/* prefix for open functions in C libraries */ +#define LUA_POF "luaopen_" + +/* separator for open functions in C libraries */ +#define LUA_OFSEP "_" + + +#define LIBPREFIX "LOADLIB: " + +#define POF LUA_POF +#define LIB_FAIL "open" + + +/* error codes for ll_loadfunc */ +#define ERRLIB 1 +#define ERRFUNC 2 + +#define setprogdir(L) ((void)0) + + +static void ll_unloadlib (void *lib); +static void *ll_load (lua_State *L, const char *path); +static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym); + + + +#if defined(LUA_DL_DLOPEN) +/* +** {======================================================================== +** This is an implementation of loadlib based on the dlfcn interface. +** The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD, +** NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least +** as an emulation layer on top of native functions. +** ========================================================================= +*/ + +#include + +static void ll_unloadlib (void *lib) { + dlclose(lib); +} + + +static void *ll_load (lua_State *L, const char *path) { + void *lib = dlopen(path, RTLD_NOW); + if (lib == NULL) lua_pushstring(L, dlerror()); + return lib; +} + + +static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { + lua_CFunction f = (lua_CFunction)dlsym(lib, sym); + if (f == NULL) lua_pushstring(L, dlerror()); + return f; +} + +/* }====================================================== */ + + + +#elif defined(LUA_DL_DLL) +/* +** {====================================================================== +** This is an implementation of loadlib for Windows using native functions. +** ======================================================================= +*/ + +#include + + +#undef setprogdir + +static void setprogdir (lua_State *L) { + char buff[MAX_PATH + 1]; + char *lb; + DWORD nsize = sizeof(buff)/sizeof(char); + DWORD n = GetModuleFileNameA(NULL, buff, nsize); + if (n == 0 || n == nsize || (lb = strrchr(buff, '\\')) == NULL) + luaL_error(L, "unable to get ModuleFileName"); + else { + *lb = '\0'; + luaL_gsub(L, lua_tostring(L, -1), LUA_EXECDIR, buff); + lua_remove(L, -2); /* remove original string */ + } +} + + +static void pusherror (lua_State *L) { + int error = GetLastError(); + char buffer[128]; + if (FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, error, 0, buffer, sizeof(buffer), NULL)) + lua_pushstring(L, buffer); + else + lua_pushfstring(L, "system error %d\n", error); +} + +static void ll_unloadlib (void *lib) { + FreeLibrary((HINSTANCE)lib); +} + + +static void *ll_load (lua_State *L, const char *path) { + HINSTANCE lib = LoadLibraryA(path); + if (lib == NULL) pusherror(L); + return lib; +} + + +static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { + lua_CFunction f = (lua_CFunction)GetProcAddress((HINSTANCE)lib, sym); + if (f == NULL) pusherror(L); + return f; +} + +/* }====================================================== */ + + + +#elif defined(LUA_DL_DYLD) +/* +** {====================================================================== +** Native Mac OS X / Darwin Implementation +** ======================================================================= +*/ + +#include + + +/* Mac appends a `_' before C function names */ +#undef POF +#define POF "_" LUA_POF + + +static void pusherror (lua_State *L) { + const char *err_str; + const char *err_file; + NSLinkEditErrors err; + int err_num; + NSLinkEditError(&err, &err_num, &err_file, &err_str); + lua_pushstring(L, err_str); +} + + +static const char *errorfromcode (NSObjectFileImageReturnCode ret) { + switch (ret) { + case NSObjectFileImageInappropriateFile: + return "file is not a bundle"; + case NSObjectFileImageArch: + return "library is for wrong CPU type"; + case NSObjectFileImageFormat: + return "bad format"; + case NSObjectFileImageAccess: + return "cannot access file"; + case NSObjectFileImageFailure: + default: + return "unable to load library"; + } +} + + +static void ll_unloadlib (void *lib) { + NSUnLinkModule((NSModule)lib, NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES); +} + + +static void *ll_load (lua_State *L, const char *path) { + NSObjectFileImage img; + NSObjectFileImageReturnCode ret; + /* this would be a rare case, but prevents crashing if it happens */ + if(!_dyld_present()) { + lua_pushliteral(L, "dyld not present"); + return NULL; + } + ret = NSCreateObjectFileImageFromFile(path, &img); + if (ret == NSObjectFileImageSuccess) { + NSModule mod = NSLinkModule(img, path, NSLINKMODULE_OPTION_PRIVATE | + NSLINKMODULE_OPTION_RETURN_ON_ERROR); + NSDestroyObjectFileImage(img); + if (mod == NULL) pusherror(L); + return mod; + } + lua_pushstring(L, errorfromcode(ret)); + return NULL; +} + + +static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { + NSSymbol nss = NSLookupSymbolInModule((NSModule)lib, sym); + if (nss == NULL) { + lua_pushfstring(L, "symbol " LUA_QS " not found", sym); + return NULL; + } + return (lua_CFunction)NSAddressOfSymbol(nss); +} + +/* }====================================================== */ + + + +#else +/* +** {====================================================== +** Fallback for other systems +** ======================================================= +*/ + +#undef LIB_FAIL +#define LIB_FAIL "absent" + + +#define DLMSG "dynamic libraries not enabled; check your Lua installation" + + +static void ll_unloadlib (void *lib) { + (void)lib; /* to avoid warnings */ +} + + +static void *ll_load (lua_State *L, const char *path) { + (void)path; /* to avoid warnings */ + lua_pushliteral(L, DLMSG); + return NULL; +} + + +static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { + (void)lib; (void)sym; /* to avoid warnings */ + lua_pushliteral(L, DLMSG); + return NULL; +} + +/* }====================================================== */ +#endif + + + +static void **ll_register (lua_State *L, const char *path) { + void **plib; + lua_pushfstring(L, "%s%s", LIBPREFIX, path); + lua_gettable(L, LUA_REGISTRYINDEX); /* check library in registry? */ + if (!lua_isnil(L, -1)) /* is there an entry? */ + plib = (void **)lua_touserdata(L, -1); + else { /* no entry yet; create one */ + lua_pop(L, 1); + plib = (void **)lua_newuserdata(L, sizeof(const void *)); + *plib = NULL; + luaL_getmetatable(L, "_LOADLIB"); + lua_setmetatable(L, -2); + lua_pushfstring(L, "%s%s", LIBPREFIX, path); + lua_pushvalue(L, -2); + lua_settable(L, LUA_REGISTRYINDEX); + } + return plib; +} + + +/* +** __gc tag method: calls library's `ll_unloadlib' function with the lib +** handle +*/ +static int gctm (lua_State *L) { + void **lib = (void **)luaL_checkudata(L, 1, "_LOADLIB"); + if (*lib) ll_unloadlib(*lib); + *lib = NULL; /* mark library as closed */ + return 0; +} + + +static int ll_loadfunc (lua_State *L, const char *path, const char *sym) { + void **reg = ll_register(L, path); + if (*reg == NULL) *reg = ll_load(L, path); + if (*reg == NULL) + return ERRLIB; /* unable to load library */ + else { + lua_CFunction f = ll_sym(L, *reg, sym); + if (f == NULL) + return ERRFUNC; /* unable to find function */ + lua_pushcfunction(L, f); + return 0; /* return function */ + } +} + + +static int ll_loadlib (lua_State *L) { + const char *path = luaL_checkstring(L, 1); + const char *init = luaL_checkstring(L, 2); + int stat = ll_loadfunc(L, path, init); + if (stat == 0) /* no errors? */ + return 1; /* return the loaded function */ + else { /* error; error message is on stack top */ + lua_pushnil(L); + lua_insert(L, -2); + lua_pushstring(L, (stat == ERRLIB) ? LIB_FAIL : "init"); + return 3; /* return nil, error message, and where */ + } +} + + + +/* +** {====================================================== +** 'require' function +** ======================================================= +*/ + + +static int readable (const char *filename) { + FILE *f = fopen(filename, "r"); /* try to open file */ + if (f == NULL) return 0; /* open failed */ + fclose(f); + return 1; +} + + +static const char *pushnexttemplate (lua_State *L, const char *path) { + const char *l; + while (*path == *LUA_PATHSEP) path++; /* skip separators */ + if (*path == '\0') return NULL; /* no more templates */ + l = strchr(path, *LUA_PATHSEP); /* find next separator */ + if (l == NULL) l = path + strlen(path); + lua_pushlstring(L, path, l - path); /* template */ + return l; +} + + +static const char *findfile (lua_State *L, const char *name, + const char *pname) { + const char *path; + name = luaL_gsub(L, name, ".", LUA_DIRSEP); + lua_getfield(L, LUA_ENVIRONINDEX, pname); + path = lua_tostring(L, -1); + if (path == NULL) + luaL_error(L, LUA_QL("package.%s") " must be a string", pname); + lua_pushliteral(L, ""); /* error accumulator */ + while ((path = pushnexttemplate(L, path)) != NULL) { + const char *filename; + filename = luaL_gsub(L, lua_tostring(L, -1), LUA_PATH_MARK, name); + lua_remove(L, -2); /* remove path template */ + if (readable(filename)) /* does file exist and is readable? */ + return filename; /* return that file name */ + lua_pushfstring(L, "\n\tno file " LUA_QS, filename); + lua_remove(L, -2); /* remove file name */ + lua_concat(L, 2); /* add entry to possible error message */ + } + return NULL; /* not found */ +} + + +static void loaderror (lua_State *L, const char *filename) { + luaL_error(L, "error loading module " LUA_QS " from file " LUA_QS ":\n\t%s", + lua_tostring(L, 1), filename, lua_tostring(L, -1)); +} + + +static int loader_Lua (lua_State *L) { + const char *filename; + const char *name = luaL_checkstring(L, 1); + filename = findfile(L, name, "path"); + if (filename == NULL) return 1; /* library not found in this path */ + if (luaL_loadfile(L, filename) != 0) + loaderror(L, filename); + return 1; /* library loaded successfully */ +} + + +static const char *mkfuncname (lua_State *L, const char *modname) { + const char *funcname; + const char *mark = strchr(modname, *LUA_IGMARK); + if (mark) modname = mark + 1; + funcname = luaL_gsub(L, modname, ".", LUA_OFSEP); + funcname = lua_pushfstring(L, POF"%s", funcname); + lua_remove(L, -2); /* remove 'gsub' result */ + return funcname; +} + + +static int loader_C (lua_State *L) { + const char *funcname; + const char *name = luaL_checkstring(L, 1); + const char *filename = findfile(L, name, "cpath"); + if (filename == NULL) return 1; /* library not found in this path */ + funcname = mkfuncname(L, name); + if (ll_loadfunc(L, filename, funcname) != 0) + loaderror(L, filename); + return 1; /* library loaded successfully */ +} + + +static int loader_Croot (lua_State *L) { + const char *funcname; + const char *filename; + const char *name = luaL_checkstring(L, 1); + const char *p = strchr(name, '.'); + int stat; + if (p == NULL) return 0; /* is root */ + lua_pushlstring(L, name, p - name); + filename = findfile(L, lua_tostring(L, -1), "cpath"); + if (filename == NULL) return 1; /* root not found */ + funcname = mkfuncname(L, name); + if ((stat = ll_loadfunc(L, filename, funcname)) != 0) { + if (stat != ERRFUNC) loaderror(L, filename); /* real error */ + lua_pushfstring(L, "\n\tno module " LUA_QS " in file " LUA_QS, + name, filename); + return 1; /* function not found */ + } + return 1; +} + + +static int loader_preload (lua_State *L) { + const char *name = luaL_checkstring(L, 1); + lua_getfield(L, LUA_ENVIRONINDEX, "preload"); + if (!lua_istable(L, -1)) + luaL_error(L, LUA_QL("package.preload") " must be a table"); + lua_getfield(L, -1, name); + if (lua_isnil(L, -1)) /* not found? */ + lua_pushfstring(L, "\n\tno field package.preload['%s']", name); + return 1; +} + + +static const int sentinel_ = 0; +#define sentinel ((void *)&sentinel_) + + +static int ll_require (lua_State *L) { + const char *name = luaL_checkstring(L, 1); + int i; + lua_settop(L, 1); /* _LOADED table will be at index 2 */ + lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); + lua_getfield(L, 2, name); + if (lua_toboolean(L, -1)) { /* is it there? */ + if (lua_touserdata(L, -1) == sentinel) /* check loops */ + luaL_error(L, "loop or previous error loading module " LUA_QS, name); + return 1; /* package is already loaded */ + } + /* else must load it; iterate over available loaders */ + lua_getfield(L, LUA_ENVIRONINDEX, "loaders"); + if (!lua_istable(L, -1)) + luaL_error(L, LUA_QL("package.loaders") " must be a table"); + lua_pushliteral(L, ""); /* error message accumulator */ + for (i=1; ; i++) { + lua_rawgeti(L, -2, i); /* get a loader */ + if (lua_isnil(L, -1)) + luaL_error(L, "module " LUA_QS " not found:%s", + name, lua_tostring(L, -2)); + lua_pushstring(L, name); + lua_call(L, 1, 1); /* call it */ + if (lua_isfunction(L, -1)) /* did it find module? */ + break; /* module loaded successfully */ + else if (lua_isstring(L, -1)) /* loader returned error message? */ + lua_concat(L, 2); /* accumulate it */ + else + lua_pop(L, 1); + } + lua_pushlightuserdata(L, sentinel); + lua_setfield(L, 2, name); /* _LOADED[name] = sentinel */ + lua_pushstring(L, name); /* pass name as argument to module */ + lua_call(L, 1, 1); /* run loaded module */ + if (!lua_isnil(L, -1)) /* non-nil return? */ + lua_setfield(L, 2, name); /* _LOADED[name] = returned value */ + lua_getfield(L, 2, name); + if (lua_touserdata(L, -1) == sentinel) { /* module did not set a value? */ + lua_pushboolean(L, 1); /* use true as result */ + lua_pushvalue(L, -1); /* extra copy to be returned */ + lua_setfield(L, 2, name); /* _LOADED[name] = true */ + } + return 1; +} + +/* }====================================================== */ + + + +/* +** {====================================================== +** 'module' function +** ======================================================= +*/ + + +static void setfenv (lua_State *L) { + lua_Debug ar; + if (lua_getstack(L, 1, &ar) == 0 || + lua_getinfo(L, "f", &ar) == 0 || /* get calling function */ + lua_iscfunction(L, -1)) + luaL_error(L, LUA_QL("module") " not called from a Lua function"); + lua_pushvalue(L, -2); + lua_setfenv(L, -2); + lua_pop(L, 1); +} + + +static void dooptions (lua_State *L, int n) { + int i; + for (i = 2; i <= n; i++) { + lua_pushvalue(L, i); /* get option (a function) */ + lua_pushvalue(L, -2); /* module */ + lua_call(L, 1, 0); + } +} + + +static void modinit (lua_State *L, const char *modname) { + const char *dot; + lua_pushvalue(L, -1); + lua_setfield(L, -2, "_M"); /* module._M = module */ + lua_pushstring(L, modname); + lua_setfield(L, -2, "_NAME"); + dot = strrchr(modname, '.'); /* look for last dot in module name */ + if (dot == NULL) dot = modname; + else dot++; + /* set _PACKAGE as package name (full module name minus last part) */ + lua_pushlstring(L, modname, dot - modname); + lua_setfield(L, -2, "_PACKAGE"); +} + + +static int ll_module (lua_State *L) { + const char *modname = luaL_checkstring(L, 1); + int loaded = lua_gettop(L) + 1; /* index of _LOADED table */ + lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); + lua_getfield(L, loaded, modname); /* get _LOADED[modname] */ + if (!lua_istable(L, -1)) { /* not found? */ + lua_pop(L, 1); /* remove previous result */ + /* try global variable (and create one if it does not exist) */ + if (luaL_findtable(L, LUA_GLOBALSINDEX, modname, 1) != NULL) + return luaL_error(L, "name conflict for module " LUA_QS, modname); + lua_pushvalue(L, -1); + lua_setfield(L, loaded, modname); /* _LOADED[modname] = new table */ + } + /* check whether table already has a _NAME field */ + lua_getfield(L, -1, "_NAME"); + if (!lua_isnil(L, -1)) /* is table an initialized module? */ + lua_pop(L, 1); + else { /* no; initialize it */ + lua_pop(L, 1); + modinit(L, modname); + } + lua_pushvalue(L, -1); + setfenv(L); + dooptions(L, loaded - 1); + return 0; +} + + +static int ll_seeall (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + if (!lua_getmetatable(L, 1)) { + lua_createtable(L, 0, 1); /* create new metatable */ + lua_pushvalue(L, -1); + lua_setmetatable(L, 1); + } + lua_pushvalue(L, LUA_GLOBALSINDEX); + lua_setfield(L, -2, "__index"); /* mt.__index = _G */ + return 0; +} + + +/* }====================================================== */ + + + +/* auxiliary mark (for internal use) */ +#define AUXMARK "\1" + +static void setpath (lua_State *L, const char *fieldname, const char *envname, + const char *def) { + const char *path = getenv(envname); + if (path == NULL) /* no environment variable? */ + lua_pushstring(L, def); /* use default */ + else { + /* replace ";;" by ";AUXMARK;" and then AUXMARK by default path */ + path = luaL_gsub(L, path, LUA_PATHSEP LUA_PATHSEP, + LUA_PATHSEP AUXMARK LUA_PATHSEP); + luaL_gsub(L, path, AUXMARK, def); + lua_remove(L, -2); + } + setprogdir(L); + lua_setfield(L, -2, fieldname); +} + + +static const luaL_Reg pk_funcs[] = { + {"loadlib", ll_loadlib}, + {"seeall", ll_seeall}, + {NULL, NULL} +}; + + +static const luaL_Reg ll_funcs[] = { + {"module", ll_module}, + {"require", ll_require}, + {NULL, NULL} +}; + + +static const lua_CFunction loaders[] = + {loader_preload, loader_Lua, loader_C, loader_Croot, NULL}; + + +LUALIB_API int luaopen_package (lua_State *L) { + int i; + /* create new type _LOADLIB */ + luaL_newmetatable(L, "_LOADLIB"); + lua_pushcfunction(L, gctm); + lua_setfield(L, -2, "__gc"); + /* create `package' table */ + luaL_register(L, LUA_LOADLIBNAME, pk_funcs); +#if defined(LUA_COMPAT_LOADLIB) + lua_getfield(L, -1, "loadlib"); + lua_setfield(L, LUA_GLOBALSINDEX, "loadlib"); +#endif + lua_pushvalue(L, -1); + lua_replace(L, LUA_ENVIRONINDEX); + /* create `loaders' table */ + lua_createtable(L, sizeof(loaders)/sizeof(loaders[0]) - 1, 0); + /* fill it with pre-defined loaders */ + for (i=0; loaders[i] != NULL; i++) { + lua_pushcfunction(L, loaders[i]); + lua_rawseti(L, -2, i+1); + } + lua_setfield(L, -2, "loaders"); /* put it in field `loaders' */ + setpath(L, "path", LUA_PATH, LUA_PATH_DEFAULT); /* set field `path' */ + setpath(L, "cpath", LUA_CPATH, LUA_CPATH_DEFAULT); /* set field `cpath' */ + /* store config information */ + lua_pushliteral(L, LUA_DIRSEP "\n" LUA_PATHSEP "\n" LUA_PATH_MARK "\n" + LUA_EXECDIR "\n" LUA_IGMARK); + lua_setfield(L, -2, "config"); + /* set field `loaded' */ + luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 2); + lua_setfield(L, -2, "loaded"); + /* set field `preload' */ + lua_newtable(L); + lua_setfield(L, -2, "preload"); + lua_pushvalue(L, LUA_GLOBALSINDEX); + luaL_register(L, NULL, ll_funcs); /* open lib into global table */ + lua_pop(L, 1); + return 1; /* return 'package' table */ +} + diff --git a/lib/lua/lobject.c b/lib/lua/lobject.c new file mode 100644 index 000000000000..4ff50732a4a9 --- /dev/null +++ b/lib/lua/lobject.c @@ -0,0 +1,214 @@ +/* +** $Id: lobject.c,v 2.22.1.1 2007/12/27 13:02:25 roberto Exp $ +** Some generic functions over Lua objects +** See Copyright Notice in lua.h +*/ + +#include +#include +#include +#include +#include + +#define lobject_c +#define LUA_CORE + +#include "lua.h" + +#include "ldo.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" +#include "lvm.h" + + + +const TValue luaO_nilobject_ = {{NULL}, LUA_TNIL}; + + +/* +** converts an integer to a "floating point byte", represented as +** (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if +** eeeee != 0 and (xxx) otherwise. +*/ +int luaO_int2fb (unsigned int x) { + int e = 0; /* expoent */ + while (x >= 16) { + x = (x+1) >> 1; + e++; + } + if (x < 8) return x; + else return ((e+1) << 3) | (cast_int(x) - 8); +} + + +/* converts back */ +int luaO_fb2int (int x) { + int e = (x >> 3) & 31; + if (e == 0) return x; + else return ((x & 7)+8) << (e - 1); +} + + +int luaO_log2 (unsigned int x) { + static const lu_byte log_2[256] = { + 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 + }; + int l = -1; + while (x >= 256) { l += 8; x >>= 8; } + return l + log_2[x]; + +} + + +int luaO_rawequalObj (const TValue *t1, const TValue *t2) { + if (ttype(t1) != ttype(t2)) return 0; + else switch (ttype(t1)) { + case LUA_TNIL: + return 1; + case LUA_TNUMBER: + return luai_numeq(nvalue(t1), nvalue(t2)); + case LUA_TBOOLEAN: + return bvalue(t1) == bvalue(t2); /* boolean true must be 1 !! */ + case LUA_TLIGHTUSERDATA: + return pvalue(t1) == pvalue(t2); + default: + lua_assert(iscollectable(t1)); + return gcvalue(t1) == gcvalue(t2); + } +} + + +int luaO_str2d (const char *s, lua_Number *result) { + char *endptr; + *result = lua_str2number(s, &endptr); + if (endptr == s) return 0; /* conversion failed */ + if (*endptr == 'x' || *endptr == 'X') /* maybe an hexadecimal constant? */ + *result = cast_num(strtoul(s, &endptr, 16)); + if (*endptr == '\0') return 1; /* most common case */ + while (isspace(cast(unsigned char, *endptr))) endptr++; + if (*endptr != '\0') return 0; /* invalid trailing characters? */ + return 1; +} + + + +static void pushstr (lua_State *L, const char *str) { + setsvalue2s(L, L->top, luaS_new(L, str)); + incr_top(L); +} + + +/* this function handles only `%d', `%c', %f, %p, and `%s' formats */ +const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { + int n = 1; + pushstr(L, ""); + for (;;) { + const char *e = strchr(fmt, '%'); + if (e == NULL) break; + setsvalue2s(L, L->top, luaS_newlstr(L, fmt, e-fmt)); + incr_top(L); + switch (*(e+1)) { + case 's': { + const char *s = va_arg(argp, char *); + if (s == NULL) s = "(null)"; + pushstr(L, s); + break; + } + case 'c': { + char buff[2]; + buff[0] = cast(char, va_arg(argp, int)); + buff[1] = '\0'; + pushstr(L, buff); + break; + } + case 'd': { + setnvalue(L->top, cast_num(va_arg(argp, int))); + incr_top(L); + break; + } + case 'f': { + setnvalue(L->top, cast_num(va_arg(argp, l_uacNumber))); + incr_top(L); + break; + } + case 'p': { + char buff[4*sizeof(void *) + 8]; /* should be enough space for a `%p' */ + sprintf(buff, "%p", va_arg(argp, void *)); + pushstr(L, buff); + break; + } + case '%': { + pushstr(L, "%"); + break; + } + default: { + char buff[3]; + buff[0] = '%'; + buff[1] = *(e+1); + buff[2] = '\0'; + pushstr(L, buff); + break; + } + } + n += 2; + fmt = e+2; + } + pushstr(L, fmt); + luaV_concat(L, n+1, cast_int(L->top - L->base) - 1); + L->top -= n; + return svalue(L->top - 1); +} + + +const char *luaO_pushfstring (lua_State *L, const char *fmt, ...) { + const char *msg; + va_list argp; + va_start(argp, fmt); + msg = luaO_pushvfstring(L, fmt, argp); + va_end(argp); + return msg; +} + + +void luaO_chunkid (char *out, const char *source, size_t bufflen) { + if (*source == '=') { + strncpy(out, source+1, bufflen); /* remove first char */ + out[bufflen-1] = '\0'; /* ensures null termination */ + } + else { /* out = "source", or "...source" */ + if (*source == '@') { + size_t l; + source++; /* skip the `@' */ + bufflen -= sizeof(" '...' "); + l = strlen(source); + strcpy(out, ""); + if (l > bufflen) { + source += (l-bufflen); /* get last part of file name */ + strcat(out, "..."); + } + strcat(out, source); + } + else { /* out = [string "string"] */ + size_t len = strcspn(source, "\n\r"); /* stop at first newline */ + bufflen -= sizeof(" [string \"...\"] "); + if (len > bufflen) len = bufflen; + strcpy(out, "[string \""); + if (source[len] != '\0') { /* must truncate? */ + strncat(out, source, len); + strcat(out, "..."); + } + else + strcat(out, source); + strcat(out, "\"]"); + } + } +} diff --git a/lib/lua/lobject.h b/lib/lua/lobject.h new file mode 100644 index 000000000000..f1e447ef3ba8 --- /dev/null +++ b/lib/lua/lobject.h @@ -0,0 +1,381 @@ +/* +** $Id: lobject.h,v 2.20.1.2 2008/08/06 13:29:48 roberto Exp $ +** Type definitions for Lua objects +** See Copyright Notice in lua.h +*/ + + +#ifndef lobject_h +#define lobject_h + + +#include + + +#include "llimits.h" +#include "lua.h" + + +/* tags for values visible from Lua */ +#define LAST_TAG LUA_TTHREAD + +#define NUM_TAGS (LAST_TAG+1) + + +/* +** Extra tags for non-values +*/ +#define LUA_TPROTO (LAST_TAG+1) +#define LUA_TUPVAL (LAST_TAG+2) +#define LUA_TDEADKEY (LAST_TAG+3) + + +/* +** Union of all collectable objects +*/ +typedef union GCObject GCObject; + + +/* +** Common Header for all collectable objects (in macro form, to be +** included in other objects) +*/ +#define CommonHeader GCObject *next; lu_byte tt; lu_byte marked + + +/* +** Common header in struct form +*/ +typedef struct GCheader { + CommonHeader; +} GCheader; + + + + +/* +** Union of all Lua values +*/ +typedef union { + GCObject *gc; + void *p; + lua_Number n; + int b; +} Value; + + +/* +** Tagged Values +*/ + +#define TValuefields Value value; int tt + +typedef struct lua_TValue { + TValuefields; +} TValue; + + +/* Macros to test type */ +#define ttisnil(o) (ttype(o) == LUA_TNIL) +#define ttisnumber(o) (ttype(o) == LUA_TNUMBER) +#define ttisstring(o) (ttype(o) == LUA_TSTRING) +#define ttistable(o) (ttype(o) == LUA_TTABLE) +#define ttisfunction(o) (ttype(o) == LUA_TFUNCTION) +#define ttisboolean(o) (ttype(o) == LUA_TBOOLEAN) +#define ttisuserdata(o) (ttype(o) == LUA_TUSERDATA) +#define ttisthread(o) (ttype(o) == LUA_TTHREAD) +#define ttislightuserdata(o) (ttype(o) == LUA_TLIGHTUSERDATA) + +/* Macros to access values */ +#define ttype(o) ((o)->tt) +#define gcvalue(o) check_exp(iscollectable(o), (o)->value.gc) +#define pvalue(o) check_exp(ttislightuserdata(o), (o)->value.p) +#define nvalue(o) check_exp(ttisnumber(o), (o)->value.n) +#define rawtsvalue(o) check_exp(ttisstring(o), &(o)->value.gc->ts) +#define tsvalue(o) (&rawtsvalue(o)->tsv) +#define rawuvalue(o) check_exp(ttisuserdata(o), &(o)->value.gc->u) +#define uvalue(o) (&rawuvalue(o)->uv) +#define clvalue(o) check_exp(ttisfunction(o), &(o)->value.gc->cl) +#define hvalue(o) check_exp(ttistable(o), &(o)->value.gc->h) +#define bvalue(o) check_exp(ttisboolean(o), (o)->value.b) +#define thvalue(o) check_exp(ttisthread(o), &(o)->value.gc->th) + +#define l_isfalse(o) (ttisnil(o) || (ttisboolean(o) && bvalue(o) == 0)) + +/* +** for internal debug only +*/ +#define checkconsistency(obj) \ + lua_assert(!iscollectable(obj) || (ttype(obj) == (obj)->value.gc->gch.tt)) + +#define checkliveness(g,obj) \ + lua_assert(!iscollectable(obj) || \ + ((ttype(obj) == (obj)->value.gc->gch.tt) && !isdead(g, (obj)->value.gc))) + + +/* Macros to set values */ +#define setnilvalue(obj) ((obj)->tt=LUA_TNIL) + +#define setnvalue(obj,x) \ + { TValue *i_o=(obj); i_o->value.n=(x); i_o->tt=LUA_TNUMBER; } + +#define setpvalue(obj,x) \ + { TValue *i_o=(obj); i_o->value.p=(x); i_o->tt=LUA_TLIGHTUSERDATA; } + +#define setbvalue(obj,x) \ + { TValue *i_o=(obj); i_o->value.b=(x); i_o->tt=LUA_TBOOLEAN; } + +#define setsvalue(L,obj,x) \ + { TValue *i_o=(obj); \ + i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TSTRING; \ + checkliveness(G(L),i_o); } + +#define setuvalue(L,obj,x) \ + { TValue *i_o=(obj); \ + i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TUSERDATA; \ + checkliveness(G(L),i_o); } + +#define setthvalue(L,obj,x) \ + { TValue *i_o=(obj); \ + i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTHREAD; \ + checkliveness(G(L),i_o); } + +#define setclvalue(L,obj,x) \ + { TValue *i_o=(obj); \ + i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TFUNCTION; \ + checkliveness(G(L),i_o); } + +#define sethvalue(L,obj,x) \ + { TValue *i_o=(obj); \ + i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTABLE; \ + checkliveness(G(L),i_o); } + +#define setptvalue(L,obj,x) \ + { TValue *i_o=(obj); \ + i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TPROTO; \ + checkliveness(G(L),i_o); } + + + + +#define setobj(L,obj1,obj2) \ + { const TValue *o2=(obj2); TValue *o1=(obj1); \ + o1->value = o2->value; o1->tt=o2->tt; \ + checkliveness(G(L),o1); } + + +/* +** different types of sets, according to destination +*/ + +/* from stack to (same) stack */ +#define setobjs2s setobj +/* to stack (not from same stack) */ +#define setobj2s setobj +#define setsvalue2s setsvalue +#define sethvalue2s sethvalue +#define setptvalue2s setptvalue +/* from table to same table */ +#define setobjt2t setobj +/* to table */ +#define setobj2t setobj +/* to new object */ +#define setobj2n setobj +#define setsvalue2n setsvalue + +#define setttype(obj, tt) (ttype(obj) = (tt)) + + +#define iscollectable(o) (ttype(o) >= LUA_TSTRING) + + + +typedef TValue *StkId; /* index to stack elements */ + + +/* +** String headers for string table +*/ +typedef union TString { + L_Umaxalign dummy; /* ensures maximum alignment for strings */ + struct { + CommonHeader; + lu_byte reserved; + unsigned int hash; + size_t len; + } tsv; +} TString; + + +#define getstr(ts) cast(const char *, (ts) + 1) +#define svalue(o) getstr(rawtsvalue(o)) + + + +typedef union Udata { + L_Umaxalign dummy; /* ensures maximum alignment for `local' udata */ + struct { + CommonHeader; + struct Table *metatable; + struct Table *env; + size_t len; + } uv; +} Udata; + + + + +/* +** Function Prototypes +*/ +typedef struct Proto { + CommonHeader; + TValue *k; /* constants used by the function */ + Instruction *code; + struct Proto **p; /* functions defined inside the function */ + int *lineinfo; /* map from opcodes to source lines */ + struct LocVar *locvars; /* information about local variables */ + TString **upvalues; /* upvalue names */ + TString *source; + int sizeupvalues; + int sizek; /* size of `k' */ + int sizecode; + int sizelineinfo; + int sizep; /* size of `p' */ + int sizelocvars; + int linedefined; + int lastlinedefined; + GCObject *gclist; + lu_byte nups; /* number of upvalues */ + lu_byte numparams; + lu_byte is_vararg; + lu_byte maxstacksize; +} Proto; + + +/* masks for new-style vararg */ +#define VARARG_HASARG 1 +#define VARARG_ISVARARG 2 +#define VARARG_NEEDSARG 4 + + +typedef struct LocVar { + TString *varname; + int startpc; /* first point where variable is active */ + int endpc; /* first point where variable is dead */ +} LocVar; + + + +/* +** Upvalues +*/ + +typedef struct UpVal { + CommonHeader; + TValue *v; /* points to stack or to its own value */ + union { + TValue value; /* the value (when closed) */ + struct { /* double linked list (when open) */ + struct UpVal *prev; + struct UpVal *next; + } l; + } u; +} UpVal; + + +/* +** Closures +*/ + +#define ClosureHeader \ + CommonHeader; lu_byte isC; lu_byte nupvalues; GCObject *gclist; \ + struct Table *env + +typedef struct CClosure { + ClosureHeader; + lua_CFunction f; + TValue upvalue[1]; +} CClosure; + + +typedef struct LClosure { + ClosureHeader; + struct Proto *p; + UpVal *upvals[1]; +} LClosure; + + +typedef union Closure { + CClosure c; + LClosure l; +} Closure; + + +#define iscfunction(o) (ttype(o) == LUA_TFUNCTION && clvalue(o)->c.isC) +#define isLfunction(o) (ttype(o) == LUA_TFUNCTION && !clvalue(o)->c.isC) + + +/* +** Tables +*/ + +typedef union TKey { + struct { + TValuefields; + struct Node *next; /* for chaining */ + } nk; + TValue tvk; +} TKey; + + +typedef struct Node { + TValue i_val; + TKey i_key; +} Node; + + +typedef struct Table { + CommonHeader; + lu_byte flags; /* 1<

lsizenode)) + + +#define luaO_nilobject (&luaO_nilobject_) + +LUAI_DATA const TValue luaO_nilobject_; + +#define ceillog2(x) (luaO_log2((x)-1) + 1) + +LUAI_FUNC int luaO_log2 (unsigned int x); +LUAI_FUNC int luaO_int2fb (unsigned int x); +LUAI_FUNC int luaO_fb2int (int x); +LUAI_FUNC int luaO_rawequalObj (const TValue *t1, const TValue *t2); +LUAI_FUNC int luaO_str2d (const char *s, lua_Number *result); +LUAI_FUNC const char *luaO_pushvfstring (lua_State *L, const char *fmt, + va_list argp); +LUAI_FUNC const char *luaO_pushfstring (lua_State *L, const char *fmt, ...); +LUAI_FUNC void luaO_chunkid (char *out, const char *source, size_t len); + + +#endif + diff --git a/lib/lua/lopcodes.c b/lib/lua/lopcodes.c new file mode 100644 index 000000000000..4cc745230b79 --- /dev/null +++ b/lib/lua/lopcodes.c @@ -0,0 +1,102 @@ +/* +** $Id: lopcodes.c,v 1.37.1.1 2007/12/27 13:02:25 roberto Exp $ +** See Copyright Notice in lua.h +*/ + + +#define lopcodes_c +#define LUA_CORE + + +#include "lopcodes.h" + + +/* ORDER OP */ + +const char *const luaP_opnames[NUM_OPCODES+1] = { + "MOVE", + "LOADK", + "LOADBOOL", + "LOADNIL", + "GETUPVAL", + "GETGLOBAL", + "GETTABLE", + "SETGLOBAL", + "SETUPVAL", + "SETTABLE", + "NEWTABLE", + "SELF", + "ADD", + "SUB", + "MUL", + "DIV", + "MOD", + "POW", + "UNM", + "NOT", + "LEN", + "CONCAT", + "JMP", + "EQ", + "LT", + "LE", + "TEST", + "TESTSET", + "CALL", + "TAILCALL", + "RETURN", + "FORLOOP", + "FORPREP", + "TFORLOOP", + "SETLIST", + "CLOSE", + "CLOSURE", + "VARARG", + NULL +}; + + +#define opmode(t,a,b,c,m) (((t)<<7) | ((a)<<6) | ((b)<<4) | ((c)<<2) | (m)) + +const lu_byte luaP_opmodes[NUM_OPCODES] = { +/* T A B C mode opcode */ + opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_MOVE */ + ,opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_LOADK */ + ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_LOADBOOL */ + ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LOADNIL */ + ,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_GETUPVAL */ + ,opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_GETGLOBAL */ + ,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_GETTABLE */ + ,opmode(0, 0, OpArgK, OpArgN, iABx) /* OP_SETGLOBAL */ + ,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_SETUPVAL */ + ,opmode(0, 0, OpArgK, OpArgK, iABC) /* OP_SETTABLE */ + ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_NEWTABLE */ + ,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_SELF */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_ADD */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_SUB */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MUL */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_DIV */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MOD */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_POW */ + ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_UNM */ + ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_NOT */ + ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LEN */ + ,opmode(0, 1, OpArgR, OpArgR, iABC) /* OP_CONCAT */ + ,opmode(0, 0, OpArgR, OpArgN, iAsBx) /* OP_JMP */ + ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_EQ */ + ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LT */ + ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LE */ + ,opmode(1, 1, OpArgR, OpArgU, iABC) /* OP_TEST */ + ,opmode(1, 1, OpArgR, OpArgU, iABC) /* OP_TESTSET */ + ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_CALL */ + ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_TAILCALL */ + ,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_RETURN */ + ,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORLOOP */ + ,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORPREP */ + ,opmode(1, 0, OpArgN, OpArgU, iABC) /* OP_TFORLOOP */ + ,opmode(0, 0, OpArgU, OpArgU, iABC) /* OP_SETLIST */ + ,opmode(0, 0, OpArgN, OpArgN, iABC) /* OP_CLOSE */ + ,opmode(0, 1, OpArgU, OpArgN, iABx) /* OP_CLOSURE */ + ,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_VARARG */ +}; + diff --git a/lib/lua/lopcodes.h b/lib/lua/lopcodes.h new file mode 100644 index 000000000000..41224d6ee14d --- /dev/null +++ b/lib/lua/lopcodes.h @@ -0,0 +1,268 @@ +/* +** $Id: lopcodes.h,v 1.125.1.1 2007/12/27 13:02:25 roberto Exp $ +** Opcodes for Lua virtual machine +** See Copyright Notice in lua.h +*/ + +#ifndef lopcodes_h +#define lopcodes_h + +#include "llimits.h" + + +/*=========================================================================== + We assume that instructions are unsigned numbers. + All instructions have an opcode in the first 6 bits. + Instructions can have the following fields: + `A' : 8 bits + `B' : 9 bits + `C' : 9 bits + `Bx' : 18 bits (`B' and `C' together) + `sBx' : signed Bx + + A signed argument is represented in excess K; that is, the number + value is the unsigned value minus K. K is exactly the maximum value + for that argument (so that -max is represented by 0, and +max is + represented by 2*max), which is half the maximum for the corresponding + unsigned argument. +===========================================================================*/ + + +enum OpMode {iABC, iABx, iAsBx}; /* basic instruction format */ + + +/* +** size and position of opcode arguments. +*/ +#define SIZE_C 9 +#define SIZE_B 9 +#define SIZE_Bx (SIZE_C + SIZE_B) +#define SIZE_A 8 + +#define SIZE_OP 6 + +#define POS_OP 0 +#define POS_A (POS_OP + SIZE_OP) +#define POS_C (POS_A + SIZE_A) +#define POS_B (POS_C + SIZE_C) +#define POS_Bx POS_C + + +/* +** limits for opcode arguments. +** we use (signed) int to manipulate most arguments, +** so they must fit in LUAI_BITSINT-1 bits (-1 for sign) +*/ +#if SIZE_Bx < LUAI_BITSINT-1 +#define MAXARG_Bx ((1<>1) /* `sBx' is signed */ +#else +#define MAXARG_Bx MAX_INT +#define MAXARG_sBx MAX_INT +#endif + + +#define MAXARG_A ((1<>POS_OP) & MASK1(SIZE_OP,0))) +#define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \ + ((cast(Instruction, o)<>POS_A) & MASK1(SIZE_A,0))) +#define SETARG_A(i,u) ((i) = (((i)&MASK0(SIZE_A,POS_A)) | \ + ((cast(Instruction, u)<>POS_B) & MASK1(SIZE_B,0))) +#define SETARG_B(i,b) ((i) = (((i)&MASK0(SIZE_B,POS_B)) | \ + ((cast(Instruction, b)<>POS_C) & MASK1(SIZE_C,0))) +#define SETARG_C(i,b) ((i) = (((i)&MASK0(SIZE_C,POS_C)) | \ + ((cast(Instruction, b)<>POS_Bx) & MASK1(SIZE_Bx,0))) +#define SETARG_Bx(i,b) ((i) = (((i)&MASK0(SIZE_Bx,POS_Bx)) | \ + ((cast(Instruction, b)< C) then pc++ */ +OP_TESTSET,/* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */ + +OP_CALL,/* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */ +OP_TAILCALL,/* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */ +OP_RETURN,/* A B return R(A), ... ,R(A+B-2) (see note) */ + +OP_FORLOOP,/* A sBx R(A)+=R(A+2); + if R(A) =) R(A)*/ +OP_CLOSURE,/* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */ + +OP_VARARG/* A B R(A), R(A+1), ..., R(A+B-1) = vararg */ +} OpCode; + + +#define NUM_OPCODES (cast(int, OP_VARARG) + 1) + + + +/*=========================================================================== + Notes: + (*) In OP_CALL, if (B == 0) then B = top. C is the number of returns - 1, + and can be 0: OP_CALL then sets `top' to last_result+1, so + next open instruction (OP_CALL, OP_RETURN, OP_SETLIST) may use `top'. + + (*) In OP_VARARG, if (B == 0) then use actual number of varargs and + set top (like in OP_CALL with C == 0). + + (*) In OP_RETURN, if (B == 0) then return up to `top' + + (*) In OP_SETLIST, if (B == 0) then B = `top'; + if (C == 0) then next `instruction' is real C + + (*) For comparisons, A specifies what condition the test should accept + (true or false). + + (*) All `skips' (pc++) assume that next instruction is a jump +===========================================================================*/ + + +/* +** masks for instruction properties. The format is: +** bits 0-1: op mode +** bits 2-3: C arg mode +** bits 4-5: B arg mode +** bit 6: instruction set register A +** bit 7: operator is a test +*/ + +enum OpArgMask { + OpArgN, /* argument is not used */ + OpArgU, /* argument is used */ + OpArgR, /* argument is a register or a jump offset */ + OpArgK /* argument is a constant or register/constant */ +}; + +LUAI_DATA const lu_byte luaP_opmodes[NUM_OPCODES]; + +#define getOpMode(m) (cast(enum OpMode, luaP_opmodes[m] & 3)) +#define getBMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 4) & 3)) +#define getCMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 2) & 3)) +#define testAMode(m) (luaP_opmodes[m] & (1 << 6)) +#define testTMode(m) (luaP_opmodes[m] & (1 << 7)) + + +LUAI_DATA const char *const luaP_opnames[NUM_OPCODES+1]; /* opcode names */ + + +/* number of list items to accumulate before a SETLIST instruction */ +#define LFIELDS_PER_FLUSH 50 + + +#endif diff --git a/lib/lua/loslib.c b/lib/lua/loslib.c new file mode 100644 index 000000000000..da06a572acf6 --- /dev/null +++ b/lib/lua/loslib.c @@ -0,0 +1,243 @@ +/* +** $Id: loslib.c,v 1.19.1.3 2008/01/18 16:38:18 roberto Exp $ +** Standard Operating System library +** See Copyright Notice in lua.h +*/ + + +#include +#include +#include +#include +#include + +#define loslib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +static int os_pushresult (lua_State *L, int i, const char *filename) { + int en = errno; /* calls to Lua API may change this value */ + if (i) { + lua_pushboolean(L, 1); + return 1; + } + else { + lua_pushnil(L); + lua_pushfstring(L, "%s: %s", filename, strerror(en)); + lua_pushinteger(L, en); + return 3; + } +} + + +static int os_execute (lua_State *L) { + lua_pushinteger(L, system(luaL_optstring(L, 1, NULL))); + return 1; +} + + +static int os_remove (lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + return os_pushresult(L, remove(filename) == 0, filename); +} + + +static int os_rename (lua_State *L) { + const char *fromname = luaL_checkstring(L, 1); + const char *toname = luaL_checkstring(L, 2); + return os_pushresult(L, rename(fromname, toname) == 0, fromname); +} + + +static int os_tmpname (lua_State *L) { + char buff[LUA_TMPNAMBUFSIZE]; + int err; + lua_tmpnam(buff, err); + if (err) + return luaL_error(L, "unable to generate a unique filename"); + lua_pushstring(L, buff); + return 1; +} + + +static int os_getenv (lua_State *L) { + lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */ + return 1; +} + + +static int os_clock (lua_State *L) { + lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC); + return 1; +} + + +/* +** {====================================================== +** Time/Date operations +** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S, +** wday=%w+1, yday=%j, isdst=? } +** ======================================================= +*/ + +static void setfield (lua_State *L, const char *key, int value) { + lua_pushinteger(L, value); + lua_setfield(L, -2, key); +} + +static void setboolfield (lua_State *L, const char *key, int value) { + if (value < 0) /* undefined? */ + return; /* does not set field */ + lua_pushboolean(L, value); + lua_setfield(L, -2, key); +} + +static int getboolfield (lua_State *L, const char *key) { + int res; + lua_getfield(L, -1, key); + res = lua_isnil(L, -1) ? -1 : lua_toboolean(L, -1); + lua_pop(L, 1); + return res; +} + + +static int getfield (lua_State *L, const char *key, int d) { + int res; + lua_getfield(L, -1, key); + if (lua_isnumber(L, -1)) + res = (int)lua_tointeger(L, -1); + else { + if (d < 0) + return luaL_error(L, "field " LUA_QS " missing in date table", key); + res = d; + } + lua_pop(L, 1); + return res; +} + + +static int os_date (lua_State *L) { + const char *s = luaL_optstring(L, 1, "%c"); + time_t t = luaL_opt(L, (time_t)luaL_checknumber, 2, time(NULL)); + struct tm *stm; + if (*s == '!') { /* UTC? */ + stm = gmtime(&t); + s++; /* skip `!' */ + } + else + stm = localtime(&t); + if (stm == NULL) /* invalid date? */ + lua_pushnil(L); + else if (strcmp(s, "*t") == 0) { + lua_createtable(L, 0, 9); /* 9 = number of fields */ + setfield(L, "sec", stm->tm_sec); + setfield(L, "min", stm->tm_min); + setfield(L, "hour", stm->tm_hour); + setfield(L, "day", stm->tm_mday); + setfield(L, "month", stm->tm_mon+1); + setfield(L, "year", stm->tm_year+1900); + setfield(L, "wday", stm->tm_wday+1); + setfield(L, "yday", stm->tm_yday+1); + setboolfield(L, "isdst", stm->tm_isdst); + } + else { + char cc[3]; + luaL_Buffer b; + cc[0] = '%'; cc[2] = '\0'; + luaL_buffinit(L, &b); + for (; *s; s++) { + if (*s != '%' || *(s + 1) == '\0') /* no conversion specifier? */ + luaL_addchar(&b, *s); + else { + size_t reslen; + char buff[200]; /* should be big enough for any conversion result */ + cc[1] = *(++s); + reslen = strftime(buff, sizeof(buff), cc, stm); + luaL_addlstring(&b, buff, reslen); + } + } + luaL_pushresult(&b); + } + return 1; +} + + +static int os_time (lua_State *L) { + time_t t; + if (lua_isnoneornil(L, 1)) /* called without args? */ + t = time(NULL); /* get current time */ + else { + struct tm ts; + luaL_checktype(L, 1, LUA_TTABLE); + lua_settop(L, 1); /* make sure table is at the top */ + ts.tm_sec = getfield(L, "sec", 0); + ts.tm_min = getfield(L, "min", 0); + ts.tm_hour = getfield(L, "hour", 12); + ts.tm_mday = getfield(L, "day", -1); + ts.tm_mon = getfield(L, "month", -1) - 1; + ts.tm_year = getfield(L, "year", -1) - 1900; + ts.tm_isdst = getboolfield(L, "isdst"); + t = mktime(&ts); + } + if (t == (time_t)(-1)) + lua_pushnil(L); + else + lua_pushnumber(L, (lua_Number)t); + return 1; +} + + +static int os_difftime (lua_State *L) { + lua_pushnumber(L, difftime((time_t)(luaL_checknumber(L, 1)), + (time_t)(luaL_optnumber(L, 2, 0)))); + return 1; +} + +/* }====================================================== */ + + +static int os_setlocale (lua_State *L) { + static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, + LC_NUMERIC, LC_TIME}; + static const char *const catnames[] = {"all", "collate", "ctype", "monetary", + "numeric", "time", NULL}; + const char *l = luaL_optstring(L, 1, NULL); + int op = luaL_checkoption(L, 2, "all", catnames); + lua_pushstring(L, setlocale(cat[op], l)); + return 1; +} + + +static int os_exit (lua_State *L) { + exit(luaL_optint(L, 1, EXIT_SUCCESS)); +} + +static const luaL_Reg syslib[] = { + {"clock", os_clock}, + {"date", os_date}, + {"difftime", os_difftime}, + {"execute", os_execute}, + {"exit", os_exit}, + {"getenv", os_getenv}, + {"remove", os_remove}, + {"rename", os_rename}, + {"setlocale", os_setlocale}, + {"time", os_time}, + {"tmpname", os_tmpname}, + {NULL, NULL} +}; + +/* }====================================================== */ + + + +LUALIB_API int luaopen_os (lua_State *L) { + luaL_register(L, LUA_OSLIBNAME, syslib); + return 1; +} + diff --git a/lib/lua/lparser.c b/lib/lua/lparser.c new file mode 100644 index 000000000000..dda7488dcad9 --- /dev/null +++ b/lib/lua/lparser.c @@ -0,0 +1,1339 @@ +/* +** $Id: lparser.c,v 2.42.1.4 2011/10/21 19:31:42 roberto Exp $ +** Lua Parser +** See Copyright Notice in lua.h +*/ + + +#include + +#define lparser_c +#define LUA_CORE + +#include "lua.h" + +#include "lcode.h" +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "llex.h" +#include "lmem.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lparser.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" + + + +#define hasmultret(k) ((k) == VCALL || (k) == VVARARG) + +#define getlocvar(fs, i) ((fs)->f->locvars[(fs)->actvar[i]]) + +#define luaY_checklimit(fs,v,l,m) if ((v)>(l)) errorlimit(fs,l,m) + + +/* +** nodes for block list (list of active blocks) +*/ +typedef struct BlockCnt { + struct BlockCnt *previous; /* chain */ + int breaklist; /* list of jumps out of this loop */ + lu_byte nactvar; /* # active locals outside the breakable structure */ + lu_byte upval; /* true if some variable in the block is an upvalue */ + lu_byte isbreakable; /* true if `block' is a loop */ +} BlockCnt; + + + +/* +** prototypes for recursive non-terminal functions +*/ +static void chunk (LexState *ls); +static void expr (LexState *ls, expdesc *v); + + +static void anchor_token (LexState *ls) { + if (ls->t.token == TK_NAME || ls->t.token == TK_STRING) { + TString *ts = ls->t.seminfo.ts; + luaX_newstring(ls, getstr(ts), ts->tsv.len); + } +} + + +static void error_expected (LexState *ls, int token) { + luaX_syntaxerror(ls, + luaO_pushfstring(ls->L, LUA_QS " expected", luaX_token2str(ls, token))); +} + + +static void errorlimit (FuncState *fs, int limit, const char *what) { + const char *msg = (fs->f->linedefined == 0) ? + luaO_pushfstring(fs->L, "main function has more than %d %s", limit, what) : + luaO_pushfstring(fs->L, "function at line %d has more than %d %s", + fs->f->linedefined, limit, what); + luaX_lexerror(fs->ls, msg, 0); +} + + +static int testnext (LexState *ls, int c) { + if (ls->t.token == c) { + luaX_next(ls); + return 1; + } + else return 0; +} + + +static void check (LexState *ls, int c) { + if (ls->t.token != c) + error_expected(ls, c); +} + +static void checknext (LexState *ls, int c) { + check(ls, c); + luaX_next(ls); +} + + +#define check_condition(ls,c,msg) { if (!(c)) luaX_syntaxerror(ls, msg); } + + + +static void check_match (LexState *ls, int what, int who, int where) { + if (!testnext(ls, what)) { + if (where == ls->linenumber) + error_expected(ls, what); + else { + luaX_syntaxerror(ls, luaO_pushfstring(ls->L, + LUA_QS " expected (to close " LUA_QS " at line %d)", + luaX_token2str(ls, what), luaX_token2str(ls, who), where)); + } + } +} + + +static TString *str_checkname (LexState *ls) { + TString *ts; + check(ls, TK_NAME); + ts = ls->t.seminfo.ts; + luaX_next(ls); + return ts; +} + + +static void init_exp (expdesc *e, expkind k, int i) { + e->f = e->t = NO_JUMP; + e->k = k; + e->u.s.info = i; +} + + +static void codestring (LexState *ls, expdesc *e, TString *s) { + init_exp(e, VK, luaK_stringK(ls->fs, s)); +} + + +static void checkname(LexState *ls, expdesc *e) { + codestring(ls, e, str_checkname(ls)); +} + + +static int registerlocalvar (LexState *ls, TString *varname) { + FuncState *fs = ls->fs; + Proto *f = fs->f; + int oldsize = f->sizelocvars; + luaM_growvector(ls->L, f->locvars, fs->nlocvars, f->sizelocvars, + LocVar, SHRT_MAX, "too many local variables"); + while (oldsize < f->sizelocvars) f->locvars[oldsize++].varname = NULL; + f->locvars[fs->nlocvars].varname = varname; + luaC_objbarrier(ls->L, f, varname); + return fs->nlocvars++; +} + + +#define new_localvarliteral(ls,v,n) \ + new_localvar(ls, luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char))-1), n) + + +static void new_localvar (LexState *ls, TString *name, int n) { + FuncState *fs = ls->fs; + luaY_checklimit(fs, fs->nactvar+n+1, LUAI_MAXVARS, "local variables"); + fs->actvar[fs->nactvar+n] = cast(unsigned short, registerlocalvar(ls, name)); +} + + +static void adjustlocalvars (LexState *ls, int nvars) { + FuncState *fs = ls->fs; + fs->nactvar = cast_byte(fs->nactvar + nvars); + for (; nvars; nvars--) { + getlocvar(fs, fs->nactvar - nvars).startpc = fs->pc; + } +} + + +static void removevars (LexState *ls, int tolevel) { + FuncState *fs = ls->fs; + while (fs->nactvar > tolevel) + getlocvar(fs, --fs->nactvar).endpc = fs->pc; +} + + +static int indexupvalue (FuncState *fs, TString *name, expdesc *v) { + int i; + Proto *f = fs->f; + int oldsize = f->sizeupvalues; + for (i=0; inups; i++) { + if (fs->upvalues[i].k == v->k && fs->upvalues[i].info == v->u.s.info) { + lua_assert(f->upvalues[i] == name); + return i; + } + } + /* new one */ + luaY_checklimit(fs, f->nups + 1, LUAI_MAXUPVALUES, "upvalues"); + luaM_growvector(fs->L, f->upvalues, f->nups, f->sizeupvalues, + TString *, MAX_INT, ""); + while (oldsize < f->sizeupvalues) f->upvalues[oldsize++] = NULL; + f->upvalues[f->nups] = name; + luaC_objbarrier(fs->L, f, name); + lua_assert(v->k == VLOCAL || v->k == VUPVAL); + fs->upvalues[f->nups].k = cast_byte(v->k); + fs->upvalues[f->nups].info = cast_byte(v->u.s.info); + return f->nups++; +} + + +static int searchvar (FuncState *fs, TString *n) { + int i; + for (i=fs->nactvar-1; i >= 0; i--) { + if (n == getlocvar(fs, i).varname) + return i; + } + return -1; /* not found */ +} + + +static void markupval (FuncState *fs, int level) { + BlockCnt *bl = fs->bl; + while (bl && bl->nactvar > level) bl = bl->previous; + if (bl) bl->upval = 1; +} + + +static int singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) { + if (fs == NULL) { /* no more levels? */ + init_exp(var, VGLOBAL, NO_REG); /* default is global variable */ + return VGLOBAL; + } + else { + int v = searchvar(fs, n); /* look up at current level */ + if (v >= 0) { + init_exp(var, VLOCAL, v); + if (!base) + markupval(fs, v); /* local will be used as an upval */ + return VLOCAL; + } + else { /* not found at current level; try upper one */ + if (singlevaraux(fs->prev, n, var, 0) == VGLOBAL) + return VGLOBAL; + var->u.s.info = indexupvalue(fs, n, var); /* else was LOCAL or UPVAL */ + var->k = VUPVAL; /* upvalue in this level */ + return VUPVAL; + } + } +} + + +static void singlevar (LexState *ls, expdesc *var) { + TString *varname = str_checkname(ls); + FuncState *fs = ls->fs; + if (singlevaraux(fs, varname, var, 1) == VGLOBAL) + var->u.s.info = luaK_stringK(fs, varname); /* info points to global name */ +} + + +static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) { + FuncState *fs = ls->fs; + int extra = nvars - nexps; + if (hasmultret(e->k)) { + extra++; /* includes call itself */ + if (extra < 0) extra = 0; + luaK_setreturns(fs, e, extra); /* last exp. provides the difference */ + if (extra > 1) luaK_reserveregs(fs, extra-1); + } + else { + if (e->k != VVOID) luaK_exp2nextreg(fs, e); /* close last expression */ + if (extra > 0) { + int reg = fs->freereg; + luaK_reserveregs(fs, extra); + luaK_nil(fs, reg, extra); + } + } +} + + +static void enterlevel (LexState *ls) { + if (++ls->L->nCcalls > LUAI_MAXCCALLS) + luaX_lexerror(ls, "chunk has too many syntax levels", 0); +} + + +#define leavelevel(ls) ((ls)->L->nCcalls--) + + +static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isbreakable) { + bl->breaklist = NO_JUMP; + bl->isbreakable = isbreakable; + bl->nactvar = fs->nactvar; + bl->upval = 0; + bl->previous = fs->bl; + fs->bl = bl; + lua_assert(fs->freereg == fs->nactvar); +} + + +static void leaveblock (FuncState *fs) { + BlockCnt *bl = fs->bl; + fs->bl = bl->previous; + removevars(fs->ls, bl->nactvar); + if (bl->upval) + luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0); + /* a block either controls scope or breaks (never both) */ + lua_assert(!bl->isbreakable || !bl->upval); + lua_assert(bl->nactvar == fs->nactvar); + fs->freereg = fs->nactvar; /* free registers */ + luaK_patchtohere(fs, bl->breaklist); +} + + +static void pushclosure (LexState *ls, FuncState *func, expdesc *v) { + FuncState *fs = ls->fs; + Proto *f = fs->f; + int oldsize = f->sizep; + int i; + luaM_growvector(ls->L, f->p, fs->np, f->sizep, Proto *, + MAXARG_Bx, "constant table overflow"); + while (oldsize < f->sizep) f->p[oldsize++] = NULL; + f->p[fs->np++] = func->f; + luaC_objbarrier(ls->L, f, func->f); + init_exp(v, VRELOCABLE, luaK_codeABx(fs, OP_CLOSURE, 0, fs->np-1)); + for (i=0; if->nups; i++) { + OpCode o = (func->upvalues[i].k == VLOCAL) ? OP_MOVE : OP_GETUPVAL; + luaK_codeABC(fs, o, 0, func->upvalues[i].info, 0); + } +} + + +static void open_func (LexState *ls, FuncState *fs) { + lua_State *L = ls->L; + Proto *f = luaF_newproto(L); + fs->f = f; + fs->prev = ls->fs; /* linked list of funcstates */ + fs->ls = ls; + fs->L = L; + ls->fs = fs; + fs->pc = 0; + fs->lasttarget = -1; + fs->jpc = NO_JUMP; + fs->freereg = 0; + fs->nk = 0; + fs->np = 0; + fs->nlocvars = 0; + fs->nactvar = 0; + fs->bl = NULL; + f->source = ls->source; + f->maxstacksize = 2; /* registers 0/1 are always valid */ + fs->h = luaH_new(L, 0, 0); + /* anchor table of constants and prototype (to avoid being collected) */ + sethvalue2s(L, L->top, fs->h); + incr_top(L); + setptvalue2s(L, L->top, f); + incr_top(L); +} + + +static void close_func (LexState *ls) { + lua_State *L = ls->L; + FuncState *fs = ls->fs; + Proto *f = fs->f; + removevars(ls, 0); + luaK_ret(fs, 0, 0); /* final return */ + luaM_reallocvector(L, f->code, f->sizecode, fs->pc, Instruction); + f->sizecode = fs->pc; + luaM_reallocvector(L, f->lineinfo, f->sizelineinfo, fs->pc, int); + f->sizelineinfo = fs->pc; + luaM_reallocvector(L, f->k, f->sizek, fs->nk, TValue); + f->sizek = fs->nk; + luaM_reallocvector(L, f->p, f->sizep, fs->np, Proto *); + f->sizep = fs->np; + luaM_reallocvector(L, f->locvars, f->sizelocvars, fs->nlocvars, LocVar); + f->sizelocvars = fs->nlocvars; + luaM_reallocvector(L, f->upvalues, f->sizeupvalues, f->nups, TString *); + f->sizeupvalues = f->nups; + lua_assert(luaG_checkcode(f)); + lua_assert(fs->bl == NULL); + ls->fs = fs->prev; + /* last token read was anchored in defunct function; must reanchor it */ + if (fs) anchor_token(ls); + L->top -= 2; /* remove table and prototype from the stack */ +} + + +Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, const char *name) { + struct LexState lexstate; + struct FuncState funcstate; + lexstate.buff = buff; + luaX_setinput(L, &lexstate, z, luaS_new(L, name)); + open_func(&lexstate, &funcstate); + funcstate.f->is_vararg = VARARG_ISVARARG; /* main func. is always vararg */ + luaX_next(&lexstate); /* read first token */ + chunk(&lexstate); + check(&lexstate, TK_EOS); + close_func(&lexstate); + lua_assert(funcstate.prev == NULL); + lua_assert(funcstate.f->nups == 0); + lua_assert(lexstate.fs == NULL); + return funcstate.f; +} + + + +/*============================================================*/ +/* GRAMMAR RULES */ +/*============================================================*/ + + +static void field (LexState *ls, expdesc *v) { + /* field -> ['.' | ':'] NAME */ + FuncState *fs = ls->fs; + expdesc key; + luaK_exp2anyreg(fs, v); + luaX_next(ls); /* skip the dot or colon */ + checkname(ls, &key); + luaK_indexed(fs, v, &key); +} + + +static void yindex (LexState *ls, expdesc *v) { + /* index -> '[' expr ']' */ + luaX_next(ls); /* skip the '[' */ + expr(ls, v); + luaK_exp2val(ls->fs, v); + checknext(ls, ']'); +} + + +/* +** {====================================================================== +** Rules for Constructors +** ======================================================================= +*/ + + +struct ConsControl { + expdesc v; /* last list item read */ + expdesc *t; /* table descriptor */ + int nh; /* total number of `record' elements */ + int na; /* total number of array elements */ + int tostore; /* number of array elements pending to be stored */ +}; + + +static void recfield (LexState *ls, struct ConsControl *cc) { + /* recfield -> (NAME | `['exp1`]') = exp1 */ + FuncState *fs = ls->fs; + int reg = ls->fs->freereg; + expdesc key, val; + int rkkey; + if (ls->t.token == TK_NAME) { + luaY_checklimit(fs, cc->nh, MAX_INT, "items in a constructor"); + checkname(ls, &key); + } + else /* ls->t.token == '[' */ + yindex(ls, &key); + cc->nh++; + checknext(ls, '='); + rkkey = luaK_exp2RK(fs, &key); + expr(ls, &val); + luaK_codeABC(fs, OP_SETTABLE, cc->t->u.s.info, rkkey, luaK_exp2RK(fs, &val)); + fs->freereg = reg; /* free registers */ +} + + +static void closelistfield (FuncState *fs, struct ConsControl *cc) { + if (cc->v.k == VVOID) return; /* there is no list item */ + luaK_exp2nextreg(fs, &cc->v); + cc->v.k = VVOID; + if (cc->tostore == LFIELDS_PER_FLUSH) { + luaK_setlist(fs, cc->t->u.s.info, cc->na, cc->tostore); /* flush */ + cc->tostore = 0; /* no more items pending */ + } +} + + +static void lastlistfield (FuncState *fs, struct ConsControl *cc) { + if (cc->tostore == 0) return; + if (hasmultret(cc->v.k)) { + luaK_setmultret(fs, &cc->v); + luaK_setlist(fs, cc->t->u.s.info, cc->na, LUA_MULTRET); + cc->na--; /* do not count last expression (unknown number of elements) */ + } + else { + if (cc->v.k != VVOID) + luaK_exp2nextreg(fs, &cc->v); + luaK_setlist(fs, cc->t->u.s.info, cc->na, cc->tostore); + } +} + + +static void listfield (LexState *ls, struct ConsControl *cc) { + expr(ls, &cc->v); + luaY_checklimit(ls->fs, cc->na, MAX_INT, "items in a constructor"); + cc->na++; + cc->tostore++; +} + + +static void constructor (LexState *ls, expdesc *t) { + /* constructor -> ?? */ + FuncState *fs = ls->fs; + int line = ls->linenumber; + int pc = luaK_codeABC(fs, OP_NEWTABLE, 0, 0, 0); + struct ConsControl cc; + cc.na = cc.nh = cc.tostore = 0; + cc.t = t; + init_exp(t, VRELOCABLE, pc); + init_exp(&cc.v, VVOID, 0); /* no value (yet) */ + luaK_exp2nextreg(ls->fs, t); /* fix it at stack top (for gc) */ + checknext(ls, '{'); + do { + lua_assert(cc.v.k == VVOID || cc.tostore > 0); + if (ls->t.token == '}') break; + closelistfield(fs, &cc); + switch(ls->t.token) { + case TK_NAME: { /* may be listfields or recfields */ + luaX_lookahead(ls); + if (ls->lookahead.token != '=') /* expression? */ + listfield(ls, &cc); + else + recfield(ls, &cc); + break; + } + case '[': { /* constructor_item -> recfield */ + recfield(ls, &cc); + break; + } + default: { /* constructor_part -> listfield */ + listfield(ls, &cc); + break; + } + } + } while (testnext(ls, ',') || testnext(ls, ';')); + check_match(ls, '}', '{', line); + lastlistfield(fs, &cc); + SETARG_B(fs->f->code[pc], luaO_int2fb(cc.na)); /* set initial array size */ + SETARG_C(fs->f->code[pc], luaO_int2fb(cc.nh)); /* set initial table size */ +} + +/* }====================================================================== */ + + + +static void parlist (LexState *ls) { + /* parlist -> [ param { `,' param } ] */ + FuncState *fs = ls->fs; + Proto *f = fs->f; + int nparams = 0; + f->is_vararg = 0; + if (ls->t.token != ')') { /* is `parlist' not empty? */ + do { + switch (ls->t.token) { + case TK_NAME: { /* param -> NAME */ + new_localvar(ls, str_checkname(ls), nparams++); + break; + } + case TK_DOTS: { /* param -> `...' */ + luaX_next(ls); +#if defined(LUA_COMPAT_VARARG) + /* use `arg' as default name */ + new_localvarliteral(ls, "arg", nparams++); + f->is_vararg = VARARG_HASARG | VARARG_NEEDSARG; +#endif + f->is_vararg |= VARARG_ISVARARG; + break; + } + default: luaX_syntaxerror(ls, " or " LUA_QL("...") " expected"); + } + } while (!f->is_vararg && testnext(ls, ',')); + } + adjustlocalvars(ls, nparams); + f->numparams = cast_byte(fs->nactvar - (f->is_vararg & VARARG_HASARG)); + luaK_reserveregs(fs, fs->nactvar); /* reserve register for parameters */ +} + + +static void body (LexState *ls, expdesc *e, int needself, int line) { + /* body -> `(' parlist `)' chunk END */ + FuncState new_fs; + open_func(ls, &new_fs); + new_fs.f->linedefined = line; + checknext(ls, '('); + if (needself) { + new_localvarliteral(ls, "self", 0); + adjustlocalvars(ls, 1); + } + parlist(ls); + checknext(ls, ')'); + chunk(ls); + new_fs.f->lastlinedefined = ls->linenumber; + check_match(ls, TK_END, TK_FUNCTION, line); + close_func(ls); + pushclosure(ls, &new_fs, e); +} + + +static int explist1 (LexState *ls, expdesc *v) { + /* explist1 -> expr { `,' expr } */ + int n = 1; /* at least one expression */ + expr(ls, v); + while (testnext(ls, ',')) { + luaK_exp2nextreg(ls->fs, v); + expr(ls, v); + n++; + } + return n; +} + + +static void funcargs (LexState *ls, expdesc *f) { + FuncState *fs = ls->fs; + expdesc args; + int base, nparams; + int line = ls->linenumber; + switch (ls->t.token) { + case '(': { /* funcargs -> `(' [ explist1 ] `)' */ + if (line != ls->lastline) + luaX_syntaxerror(ls,"ambiguous syntax (function call x new statement)"); + luaX_next(ls); + if (ls->t.token == ')') /* arg list is empty? */ + args.k = VVOID; + else { + explist1(ls, &args); + luaK_setmultret(fs, &args); + } + check_match(ls, ')', '(', line); + break; + } + case '{': { /* funcargs -> constructor */ + constructor(ls, &args); + break; + } + case TK_STRING: { /* funcargs -> STRING */ + codestring(ls, &args, ls->t.seminfo.ts); + luaX_next(ls); /* must use `seminfo' before `next' */ + break; + } + default: { + luaX_syntaxerror(ls, "function arguments expected"); + return; + } + } + lua_assert(f->k == VNONRELOC); + base = f->u.s.info; /* base register for call */ + if (hasmultret(args.k)) + nparams = LUA_MULTRET; /* open call */ + else { + if (args.k != VVOID) + luaK_exp2nextreg(fs, &args); /* close last argument */ + nparams = fs->freereg - (base+1); + } + init_exp(f, VCALL, luaK_codeABC(fs, OP_CALL, base, nparams+1, 2)); + luaK_fixline(fs, line); + fs->freereg = base+1; /* call remove function and arguments and leaves + (unless changed) one result */ +} + + + + +/* +** {====================================================================== +** Expression parsing +** ======================================================================= +*/ + + +static void prefixexp (LexState *ls, expdesc *v) { + /* prefixexp -> NAME | '(' expr ')' */ + switch (ls->t.token) { + case '(': { + int line = ls->linenumber; + luaX_next(ls); + expr(ls, v); + check_match(ls, ')', '(', line); + luaK_dischargevars(ls->fs, v); + return; + } + case TK_NAME: { + singlevar(ls, v); + return; + } + default: { + luaX_syntaxerror(ls, "unexpected symbol"); + return; + } + } +} + + +static void primaryexp (LexState *ls, expdesc *v) { + /* primaryexp -> + prefixexp { `.' NAME | `[' exp `]' | `:' NAME funcargs | funcargs } */ + FuncState *fs = ls->fs; + prefixexp(ls, v); + for (;;) { + switch (ls->t.token) { + case '.': { /* field */ + field(ls, v); + break; + } + case '[': { /* `[' exp1 `]' */ + expdesc key; + luaK_exp2anyreg(fs, v); + yindex(ls, &key); + luaK_indexed(fs, v, &key); + break; + } + case ':': { /* `:' NAME funcargs */ + expdesc key; + luaX_next(ls); + checkname(ls, &key); + luaK_self(fs, v, &key); + funcargs(ls, v); + break; + } + case '(': case TK_STRING: case '{': { /* funcargs */ + luaK_exp2nextreg(fs, v); + funcargs(ls, v); + break; + } + default: return; + } + } +} + + +static void simpleexp (LexState *ls, expdesc *v) { + /* simpleexp -> NUMBER | STRING | NIL | true | false | ... | + constructor | FUNCTION body | primaryexp */ + switch (ls->t.token) { + case TK_NUMBER: { + init_exp(v, VKNUM, 0); + v->u.nval = ls->t.seminfo.r; + break; + } + case TK_STRING: { + codestring(ls, v, ls->t.seminfo.ts); + break; + } + case TK_NIL: { + init_exp(v, VNIL, 0); + break; + } + case TK_TRUE: { + init_exp(v, VTRUE, 0); + break; + } + case TK_FALSE: { + init_exp(v, VFALSE, 0); + break; + } + case TK_DOTS: { /* vararg */ + FuncState *fs = ls->fs; + check_condition(ls, fs->f->is_vararg, + "cannot use " LUA_QL("...") " outside a vararg function"); + fs->f->is_vararg &= ~VARARG_NEEDSARG; /* don't need 'arg' */ + init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 1, 0)); + break; + } + case '{': { /* constructor */ + constructor(ls, v); + return; + } + case TK_FUNCTION: { + luaX_next(ls); + body(ls, v, 0, ls->linenumber); + return; + } + default: { + primaryexp(ls, v); + return; + } + } + luaX_next(ls); +} + + +static UnOpr getunopr (int op) { + switch (op) { + case TK_NOT: return OPR_NOT; + case '-': return OPR_MINUS; + case '#': return OPR_LEN; + default: return OPR_NOUNOPR; + } +} + + +static BinOpr getbinopr (int op) { + switch (op) { + case '+': return OPR_ADD; + case '-': return OPR_SUB; + case '*': return OPR_MUL; + case '/': return OPR_DIV; + case '%': return OPR_MOD; + case '^': return OPR_POW; + case TK_CONCAT: return OPR_CONCAT; + case TK_NE: return OPR_NE; + case TK_EQ: return OPR_EQ; + case '<': return OPR_LT; + case TK_LE: return OPR_LE; + case '>': return OPR_GT; + case TK_GE: return OPR_GE; + case TK_AND: return OPR_AND; + case TK_OR: return OPR_OR; + default: return OPR_NOBINOPR; + } +} + + +static const struct { + lu_byte left; /* left priority for each binary operator */ + lu_byte right; /* right priority */ +} priority[] = { /* ORDER OPR */ + {6, 6}, {6, 6}, {7, 7}, {7, 7}, {7, 7}, /* `+' `-' `/' `%' */ + {10, 9}, {5, 4}, /* power and concat (right associative) */ + {3, 3}, {3, 3}, /* equality and inequality */ + {3, 3}, {3, 3}, {3, 3}, {3, 3}, /* order */ + {2, 2}, {1, 1} /* logical (and/or) */ +}; + +#define UNARY_PRIORITY 8 /* priority for unary operators */ + + +/* +** subexpr -> (simpleexp | unop subexpr) { binop subexpr } +** where `binop' is any binary operator with a priority higher than `limit' +*/ +static BinOpr subexpr (LexState *ls, expdesc *v, unsigned int limit) { + BinOpr op; + UnOpr uop; + enterlevel(ls); + uop = getunopr(ls->t.token); + if (uop != OPR_NOUNOPR) { + luaX_next(ls); + subexpr(ls, v, UNARY_PRIORITY); + luaK_prefix(ls->fs, uop, v); + } + else simpleexp(ls, v); + /* expand while operators have priorities higher than `limit' */ + op = getbinopr(ls->t.token); + while (op != OPR_NOBINOPR && priority[op].left > limit) { + expdesc v2; + BinOpr nextop; + luaX_next(ls); + luaK_infix(ls->fs, op, v); + /* read sub-expression with higher priority */ + nextop = subexpr(ls, &v2, priority[op].right); + luaK_posfix(ls->fs, op, v, &v2); + op = nextop; + } + leavelevel(ls); + return op; /* return first untreated operator */ +} + + +static void expr (LexState *ls, expdesc *v) { + subexpr(ls, v, 0); +} + +/* }==================================================================== */ + + + +/* +** {====================================================================== +** Rules for Statements +** ======================================================================= +*/ + + +static int block_follow (int token) { + switch (token) { + case TK_ELSE: case TK_ELSEIF: case TK_END: + case TK_UNTIL: case TK_EOS: + return 1; + default: return 0; + } +} + + +static void block (LexState *ls) { + /* block -> chunk */ + FuncState *fs = ls->fs; + BlockCnt bl; + enterblock(fs, &bl, 0); + chunk(ls); + lua_assert(bl.breaklist == NO_JUMP); + leaveblock(fs); +} + + +/* +** structure to chain all variables in the left-hand side of an +** assignment +*/ +struct LHS_assign { + struct LHS_assign *prev; + expdesc v; /* variable (global, local, upvalue, or indexed) */ +}; + + +/* +** check whether, in an assignment to a local variable, the local variable +** is needed in a previous assignment (to a table). If so, save original +** local value in a safe place and use this safe copy in the previous +** assignment. +*/ +static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) { + FuncState *fs = ls->fs; + int extra = fs->freereg; /* eventual position to save local variable */ + int conflict = 0; + for (; lh; lh = lh->prev) { + if (lh->v.k == VINDEXED) { + if (lh->v.u.s.info == v->u.s.info) { /* conflict? */ + conflict = 1; + lh->v.u.s.info = extra; /* previous assignment will use safe copy */ + } + if (lh->v.u.s.aux == v->u.s.info) { /* conflict? */ + conflict = 1; + lh->v.u.s.aux = extra; /* previous assignment will use safe copy */ + } + } + } + if (conflict) { + luaK_codeABC(fs, OP_MOVE, fs->freereg, v->u.s.info, 0); /* make copy */ + luaK_reserveregs(fs, 1); + } +} + + +static void assignment (LexState *ls, struct LHS_assign *lh, int nvars) { + expdesc e; + check_condition(ls, VLOCAL <= lh->v.k && lh->v.k <= VINDEXED, + "syntax error"); + if (testnext(ls, ',')) { /* assignment -> `,' primaryexp assignment */ + struct LHS_assign nv; + nv.prev = lh; + primaryexp(ls, &nv.v); + if (nv.v.k == VLOCAL) + check_conflict(ls, lh, &nv.v); + luaY_checklimit(ls->fs, nvars, LUAI_MAXCCALLS - ls->L->nCcalls, + "variables in assignment"); + assignment(ls, &nv, nvars+1); + } + else { /* assignment -> `=' explist1 */ + int nexps; + checknext(ls, '='); + nexps = explist1(ls, &e); + if (nexps != nvars) { + adjust_assign(ls, nvars, nexps, &e); + if (nexps > nvars) + ls->fs->freereg -= nexps - nvars; /* remove extra values */ + } + else { + luaK_setoneret(ls->fs, &e); /* close last expression */ + luaK_storevar(ls->fs, &lh->v, &e); + return; /* avoid default */ + } + } + init_exp(&e, VNONRELOC, ls->fs->freereg-1); /* default assignment */ + luaK_storevar(ls->fs, &lh->v, &e); +} + + +static int cond (LexState *ls) { + /* cond -> exp */ + expdesc v; + expr(ls, &v); /* read condition */ + if (v.k == VNIL) v.k = VFALSE; /* `falses' are all equal here */ + luaK_goiftrue(ls->fs, &v); + return v.f; +} + + +static void breakstat (LexState *ls) { + FuncState *fs = ls->fs; + BlockCnt *bl = fs->bl; + int upval = 0; + while (bl && !bl->isbreakable) { + upval |= bl->upval; + bl = bl->previous; + } + if (!bl) + luaX_syntaxerror(ls, "no loop to break"); + if (upval) + luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0); + luaK_concat(fs, &bl->breaklist, luaK_jump(fs)); +} + + +static void whilestat (LexState *ls, int line) { + /* whilestat -> WHILE cond DO block END */ + FuncState *fs = ls->fs; + int whileinit; + int condexit; + BlockCnt bl; + luaX_next(ls); /* skip WHILE */ + whileinit = luaK_getlabel(fs); + condexit = cond(ls); + enterblock(fs, &bl, 1); + checknext(ls, TK_DO); + block(ls); + luaK_patchlist(fs, luaK_jump(fs), whileinit); + check_match(ls, TK_END, TK_WHILE, line); + leaveblock(fs); + luaK_patchtohere(fs, condexit); /* false conditions finish the loop */ +} + + +static void repeatstat (LexState *ls, int line) { + /* repeatstat -> REPEAT block UNTIL cond */ + int condexit; + FuncState *fs = ls->fs; + int repeat_init = luaK_getlabel(fs); + BlockCnt bl1, bl2; + enterblock(fs, &bl1, 1); /* loop block */ + enterblock(fs, &bl2, 0); /* scope block */ + luaX_next(ls); /* skip REPEAT */ + chunk(ls); + check_match(ls, TK_UNTIL, TK_REPEAT, line); + condexit = cond(ls); /* read condition (inside scope block) */ + if (!bl2.upval) { /* no upvalues? */ + leaveblock(fs); /* finish scope */ + luaK_patchlist(ls->fs, condexit, repeat_init); /* close the loop */ + } + else { /* complete semantics when there are upvalues */ + breakstat(ls); /* if condition then break */ + luaK_patchtohere(ls->fs, condexit); /* else... */ + leaveblock(fs); /* finish scope... */ + luaK_patchlist(ls->fs, luaK_jump(fs), repeat_init); /* and repeat */ + } + leaveblock(fs); /* finish loop */ +} + + +static int exp1 (LexState *ls) { + expdesc e; + int k; + expr(ls, &e); + k = e.k; + luaK_exp2nextreg(ls->fs, &e); + return k; +} + + +static void forbody (LexState *ls, int base, int line, int nvars, int isnum) { + /* forbody -> DO block */ + BlockCnt bl; + FuncState *fs = ls->fs; + int prep, endfor; + adjustlocalvars(ls, 3); /* control variables */ + checknext(ls, TK_DO); + prep = isnum ? luaK_codeAsBx(fs, OP_FORPREP, base, NO_JUMP) : luaK_jump(fs); + enterblock(fs, &bl, 0); /* scope for declared variables */ + adjustlocalvars(ls, nvars); + luaK_reserveregs(fs, nvars); + block(ls); + leaveblock(fs); /* end of scope for declared variables */ + luaK_patchtohere(fs, prep); + endfor = (isnum) ? luaK_codeAsBx(fs, OP_FORLOOP, base, NO_JUMP) : + luaK_codeABC(fs, OP_TFORLOOP, base, 0, nvars); + luaK_fixline(fs, line); /* pretend that `OP_FOR' starts the loop */ + luaK_patchlist(fs, (isnum ? endfor : luaK_jump(fs)), prep + 1); +} + + +static void fornum (LexState *ls, TString *varname, int line) { + /* fornum -> NAME = exp1,exp1[,exp1] forbody */ + FuncState *fs = ls->fs; + int base = fs->freereg; + new_localvarliteral(ls, "(for index)", 0); + new_localvarliteral(ls, "(for limit)", 1); + new_localvarliteral(ls, "(for step)", 2); + new_localvar(ls, varname, 3); + checknext(ls, '='); + exp1(ls); /* initial value */ + checknext(ls, ','); + exp1(ls); /* limit */ + if (testnext(ls, ',')) + exp1(ls); /* optional step */ + else { /* default step = 1 */ + luaK_codeABx(fs, OP_LOADK, fs->freereg, luaK_numberK(fs, 1)); + luaK_reserveregs(fs, 1); + } + forbody(ls, base, line, 1, 1); +} + + +static void forlist (LexState *ls, TString *indexname) { + /* forlist -> NAME {,NAME} IN explist1 forbody */ + FuncState *fs = ls->fs; + expdesc e; + int nvars = 0; + int line; + int base = fs->freereg; + /* create control variables */ + new_localvarliteral(ls, "(for generator)", nvars++); + new_localvarliteral(ls, "(for state)", nvars++); + new_localvarliteral(ls, "(for control)", nvars++); + /* create declared variables */ + new_localvar(ls, indexname, nvars++); + while (testnext(ls, ',')) + new_localvar(ls, str_checkname(ls), nvars++); + checknext(ls, TK_IN); + line = ls->linenumber; + adjust_assign(ls, 3, explist1(ls, &e), &e); + luaK_checkstack(fs, 3); /* extra space to call generator */ + forbody(ls, base, line, nvars - 3, 0); +} + + +static void forstat (LexState *ls, int line) { + /* forstat -> FOR (fornum | forlist) END */ + FuncState *fs = ls->fs; + TString *varname; + BlockCnt bl; + enterblock(fs, &bl, 1); /* scope for loop and control variables */ + luaX_next(ls); /* skip `for' */ + varname = str_checkname(ls); /* first variable name */ + switch (ls->t.token) { + case '=': fornum(ls, varname, line); break; + case ',': case TK_IN: forlist(ls, varname); break; + default: luaX_syntaxerror(ls, LUA_QL("=") " or " LUA_QL("in") " expected"); + } + check_match(ls, TK_END, TK_FOR, line); + leaveblock(fs); /* loop scope (`break' jumps to this point) */ +} + + +static int test_then_block (LexState *ls) { + /* test_then_block -> [IF | ELSEIF] cond THEN block */ + int condexit; + luaX_next(ls); /* skip IF or ELSEIF */ + condexit = cond(ls); + checknext(ls, TK_THEN); + block(ls); /* `then' part */ + return condexit; +} + + +static void ifstat (LexState *ls, int line) { + /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */ + FuncState *fs = ls->fs; + int flist; + int escapelist = NO_JUMP; + flist = test_then_block(ls); /* IF cond THEN block */ + while (ls->t.token == TK_ELSEIF) { + luaK_concat(fs, &escapelist, luaK_jump(fs)); + luaK_patchtohere(fs, flist); + flist = test_then_block(ls); /* ELSEIF cond THEN block */ + } + if (ls->t.token == TK_ELSE) { + luaK_concat(fs, &escapelist, luaK_jump(fs)); + luaK_patchtohere(fs, flist); + luaX_next(ls); /* skip ELSE (after patch, for correct line info) */ + block(ls); /* `else' part */ + } + else + luaK_concat(fs, &escapelist, flist); + luaK_patchtohere(fs, escapelist); + check_match(ls, TK_END, TK_IF, line); +} + + +static void localfunc (LexState *ls) { + expdesc v, b; + FuncState *fs = ls->fs; + new_localvar(ls, str_checkname(ls), 0); + init_exp(&v, VLOCAL, fs->freereg); + luaK_reserveregs(fs, 1); + adjustlocalvars(ls, 1); + body(ls, &b, 0, ls->linenumber); + luaK_storevar(fs, &v, &b); + /* debug information will only see the variable after this point! */ + getlocvar(fs, fs->nactvar - 1).startpc = fs->pc; +} + + +static void localstat (LexState *ls) { + /* stat -> LOCAL NAME {`,' NAME} [`=' explist1] */ + int nvars = 0; + int nexps; + expdesc e; + do { + new_localvar(ls, str_checkname(ls), nvars++); + } while (testnext(ls, ',')); + if (testnext(ls, '=')) + nexps = explist1(ls, &e); + else { + e.k = VVOID; + nexps = 0; + } + adjust_assign(ls, nvars, nexps, &e); + adjustlocalvars(ls, nvars); +} + + +static int funcname (LexState *ls, expdesc *v) { + /* funcname -> NAME {field} [`:' NAME] */ + int needself = 0; + singlevar(ls, v); + while (ls->t.token == '.') + field(ls, v); + if (ls->t.token == ':') { + needself = 1; + field(ls, v); + } + return needself; +} + + +static void funcstat (LexState *ls, int line) { + /* funcstat -> FUNCTION funcname body */ + int needself; + expdesc v, b; + luaX_next(ls); /* skip FUNCTION */ + needself = funcname(ls, &v); + body(ls, &b, needself, line); + luaK_storevar(ls->fs, &v, &b); + luaK_fixline(ls->fs, line); /* definition `happens' in the first line */ +} + + +static void exprstat (LexState *ls) { + /* stat -> func | assignment */ + FuncState *fs = ls->fs; + struct LHS_assign v; + primaryexp(ls, &v.v); + if (v.v.k == VCALL) /* stat -> func */ + SETARG_C(getcode(fs, &v.v), 1); /* call statement uses no results */ + else { /* stat -> assignment */ + v.prev = NULL; + assignment(ls, &v, 1); + } +} + + +static void retstat (LexState *ls) { + /* stat -> RETURN explist */ + FuncState *fs = ls->fs; + expdesc e; + int first, nret; /* registers with returned values */ + luaX_next(ls); /* skip RETURN */ + if (block_follow(ls->t.token) || ls->t.token == ';') + first = nret = 0; /* return no values */ + else { + nret = explist1(ls, &e); /* optional return values */ + if (hasmultret(e.k)) { + luaK_setmultret(fs, &e); + if (e.k == VCALL && nret == 1) { /* tail call? */ + SET_OPCODE(getcode(fs,&e), OP_TAILCALL); + lua_assert(GETARG_A(getcode(fs,&e)) == fs->nactvar); + } + first = fs->nactvar; + nret = LUA_MULTRET; /* return all values */ + } + else { + if (nret == 1) /* only one single value? */ + first = luaK_exp2anyreg(fs, &e); + else { + luaK_exp2nextreg(fs, &e); /* values must go to the `stack' */ + first = fs->nactvar; /* return all `active' values */ + lua_assert(nret == fs->freereg - first); + } + } + } + luaK_ret(fs, first, nret); +} + + +static int statement (LexState *ls) { + int line = ls->linenumber; /* may be needed for error messages */ + switch (ls->t.token) { + case TK_IF: { /* stat -> ifstat */ + ifstat(ls, line); + return 0; + } + case TK_WHILE: { /* stat -> whilestat */ + whilestat(ls, line); + return 0; + } + case TK_DO: { /* stat -> DO block END */ + luaX_next(ls); /* skip DO */ + block(ls); + check_match(ls, TK_END, TK_DO, line); + return 0; + } + case TK_FOR: { /* stat -> forstat */ + forstat(ls, line); + return 0; + } + case TK_REPEAT: { /* stat -> repeatstat */ + repeatstat(ls, line); + return 0; + } + case TK_FUNCTION: { + funcstat(ls, line); /* stat -> funcstat */ + return 0; + } + case TK_LOCAL: { /* stat -> localstat */ + luaX_next(ls); /* skip LOCAL */ + if (testnext(ls, TK_FUNCTION)) /* local function? */ + localfunc(ls); + else + localstat(ls); + return 0; + } + case TK_RETURN: { /* stat -> retstat */ + retstat(ls); + return 1; /* must be last statement */ + } + case TK_BREAK: { /* stat -> breakstat */ + luaX_next(ls); /* skip BREAK */ + breakstat(ls); + return 1; /* must be last statement */ + } + default: { + exprstat(ls); + return 0; /* to avoid warnings */ + } + } +} + + +static void chunk (LexState *ls) { + /* chunk -> { stat [`;'] } */ + int islast = 0; + enterlevel(ls); + while (!islast && !block_follow(ls->t.token)) { + islast = statement(ls); + testnext(ls, ';'); + lua_assert(ls->fs->f->maxstacksize >= ls->fs->freereg && + ls->fs->freereg >= ls->fs->nactvar); + ls->fs->freereg = ls->fs->nactvar; /* free registers */ + } + leavelevel(ls); +} + +/* }====================================================================== */ diff --git a/lib/lua/lparser.h b/lib/lua/lparser.h new file mode 100644 index 000000000000..18836afd1cdf --- /dev/null +++ b/lib/lua/lparser.h @@ -0,0 +1,82 @@ +/* +** $Id: lparser.h,v 1.57.1.1 2007/12/27 13:02:25 roberto Exp $ +** Lua Parser +** See Copyright Notice in lua.h +*/ + +#ifndef lparser_h +#define lparser_h + +#include "llimits.h" +#include "lobject.h" +#include "lzio.h" + + +/* +** Expression descriptor +*/ + +typedef enum { + VVOID, /* no value */ + VNIL, + VTRUE, + VFALSE, + VK, /* info = index of constant in `k' */ + VKNUM, /* nval = numerical value */ + VLOCAL, /* info = local register */ + VUPVAL, /* info = index of upvalue in `upvalues' */ + VGLOBAL, /* info = index of table; aux = index of global name in `k' */ + VINDEXED, /* info = table register; aux = index register (or `k') */ + VJMP, /* info = instruction pc */ + VRELOCABLE, /* info = instruction pc */ + VNONRELOC, /* info = result register */ + VCALL, /* info = instruction pc */ + VVARARG /* info = instruction pc */ +} expkind; + +typedef struct expdesc { + expkind k; + union { + struct { int info, aux; } s; + lua_Number nval; + } u; + int t; /* patch list of `exit when true' */ + int f; /* patch list of `exit when false' */ +} expdesc; + + +typedef struct upvaldesc { + lu_byte k; + lu_byte info; +} upvaldesc; + + +struct BlockCnt; /* defined in lparser.c */ + + +/* state needed to generate code for a given function */ +typedef struct FuncState { + Proto *f; /* current function header */ + Table *h; /* table to find (and reuse) elements in `k' */ + struct FuncState *prev; /* enclosing function */ + struct LexState *ls; /* lexical state */ + struct lua_State *L; /* copy of the Lua state */ + struct BlockCnt *bl; /* chain of current blocks */ + int pc; /* next position to code (equivalent to `ncode') */ + int lasttarget; /* `pc' of last `jump target' */ + int jpc; /* list of pending jumps to `pc' */ + int freereg; /* first free register */ + int nk; /* number of elements in `k' */ + int np; /* number of elements in `p' */ + short nlocvars; /* number of elements in `locvars' */ + lu_byte nactvar; /* number of active local variables */ + upvaldesc upvalues[LUAI_MAXUPVALUES]; /* upvalues */ + unsigned short actvar[LUAI_MAXVARS]; /* declared-variable stack */ +} FuncState; + + +LUAI_FUNC Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, + const char *name); + + +#endif diff --git a/lib/lua/lstate.c b/lib/lua/lstate.c new file mode 100644 index 000000000000..4313b83a0c7c --- /dev/null +++ b/lib/lua/lstate.c @@ -0,0 +1,214 @@ +/* +** $Id: lstate.c,v 2.36.1.2 2008/01/03 15:20:39 roberto Exp $ +** Global State +** See Copyright Notice in lua.h +*/ + + +#include + +#define lstate_c +#define LUA_CORE + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "llex.h" +#include "lmem.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" + + +#define state_size(x) (sizeof(x) + LUAI_EXTRASPACE) +#define fromstate(l) (cast(lu_byte *, (l)) - LUAI_EXTRASPACE) +#define tostate(l) (cast(lua_State *, cast(lu_byte *, l) + LUAI_EXTRASPACE)) + + +/* +** Main thread combines a thread state and the global state +*/ +typedef struct LG { + lua_State l; + global_State g; +} LG; + + + +static void stack_init (lua_State *L1, lua_State *L) { + /* initialize CallInfo array */ + L1->base_ci = luaM_newvector(L, BASIC_CI_SIZE, CallInfo); + L1->ci = L1->base_ci; + L1->size_ci = BASIC_CI_SIZE; + L1->end_ci = L1->base_ci + L1->size_ci - 1; + /* initialize stack array */ + L1->stack = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, TValue); + L1->stacksize = BASIC_STACK_SIZE + EXTRA_STACK; + L1->top = L1->stack; + L1->stack_last = L1->stack+(L1->stacksize - EXTRA_STACK)-1; + /* initialize first ci */ + L1->ci->func = L1->top; + setnilvalue(L1->top++); /* `function' entry for this `ci' */ + L1->base = L1->ci->base = L1->top; + L1->ci->top = L1->top + LUA_MINSTACK; +} + + +static void freestack (lua_State *L, lua_State *L1) { + luaM_freearray(L, L1->base_ci, L1->size_ci, CallInfo); + luaM_freearray(L, L1->stack, L1->stacksize, TValue); +} + + +/* +** open parts that may cause memory-allocation errors +*/ +static void f_luaopen (lua_State *L, void *ud) { + global_State *g = G(L); + UNUSED(ud); + stack_init(L, L); /* init stack */ + sethvalue(L, gt(L), luaH_new(L, 0, 2)); /* table of globals */ + sethvalue(L, registry(L), luaH_new(L, 0, 2)); /* registry */ + luaS_resize(L, MINSTRTABSIZE); /* initial size of string table */ + luaT_init(L); + luaX_init(L); + luaS_fix(luaS_newliteral(L, MEMERRMSG)); + g->GCthreshold = 4*g->totalbytes; +} + + +static void preinit_state (lua_State *L, global_State *g) { + G(L) = g; + L->stack = NULL; + L->stacksize = 0; + L->errorJmp = NULL; + L->hook = NULL; + L->hookmask = 0; + L->basehookcount = 0; + L->allowhook = 1; + resethookcount(L); + L->openupval = NULL; + L->size_ci = 0; + L->nCcalls = L->baseCcalls = 0; + L->status = 0; + L->base_ci = L->ci = NULL; + L->savedpc = NULL; + L->errfunc = 0; + setnilvalue(gt(L)); +} + + +static void close_state (lua_State *L) { + global_State *g = G(L); + luaF_close(L, L->stack); /* close all upvalues for this thread */ + luaC_freeall(L); /* collect all objects */ + lua_assert(g->rootgc == obj2gco(L)); + lua_assert(g->strt.nuse == 0); + luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size, TString *); + luaZ_freebuffer(L, &g->buff); + freestack(L, L); + lua_assert(g->totalbytes == sizeof(LG)); + (*g->frealloc)(g->ud, fromstate(L), state_size(LG), 0); +} + + +lua_State *luaE_newthread (lua_State *L) { + lua_State *L1 = tostate(luaM_malloc(L, state_size(lua_State))); + luaC_link(L, obj2gco(L1), LUA_TTHREAD); + preinit_state(L1, G(L)); + stack_init(L1, L); /* init stack */ + setobj2n(L, gt(L1), gt(L)); /* share table of globals */ + L1->hookmask = L->hookmask; + L1->basehookcount = L->basehookcount; + L1->hook = L->hook; + resethookcount(L1); + lua_assert(iswhite(obj2gco(L1))); + return L1; +} + + +void luaE_freethread (lua_State *L, lua_State *L1) { + luaF_close(L1, L1->stack); /* close all upvalues for this thread */ + lua_assert(L1->openupval == NULL); + luai_userstatefree(L1); + freestack(L, L1); + luaM_freemem(L, fromstate(L1), state_size(lua_State)); +} + + +LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { + int i; + lua_State *L; + global_State *g; + void *l = (*f)(ud, NULL, 0, state_size(LG)); + if (l == NULL) return NULL; + L = tostate(l); + g = &((LG *)L)->g; + L->next = NULL; + L->tt = LUA_TTHREAD; + g->currentwhite = bit2mask(WHITE0BIT, FIXEDBIT); + L->marked = luaC_white(g); + set2bits(L->marked, FIXEDBIT, SFIXEDBIT); + preinit_state(L, g); + g->frealloc = f; + g->ud = ud; + g->mainthread = L; + g->uvhead.u.l.prev = &g->uvhead; + g->uvhead.u.l.next = &g->uvhead; + g->GCthreshold = 0; /* mark it as unfinished state */ + g->strt.size = 0; + g->strt.nuse = 0; + g->strt.hash = NULL; + setnilvalue(registry(L)); + luaZ_initbuffer(L, &g->buff); + g->panic = NULL; + g->gcstate = GCSpause; + g->rootgc = obj2gco(L); + g->sweepstrgc = 0; + g->sweepgc = &g->rootgc; + g->gray = NULL; + g->grayagain = NULL; + g->weak = NULL; + g->tmudata = NULL; + g->totalbytes = sizeof(LG); + g->gcpause = LUAI_GCPAUSE; + g->gcstepmul = LUAI_GCMUL; + g->gcdept = 0; + for (i=0; imt[i] = NULL; + if (luaD_rawrunprotected(L, f_luaopen, NULL) != 0) { + /* memory allocation error: free partial state */ + close_state(L); + L = NULL; + } + else + luai_userstateopen(L); + return L; +} + + +static void callallgcTM (lua_State *L, void *ud) { + UNUSED(ud); + luaC_callGCTM(L); /* call GC metamethods for all udata */ +} + + +LUA_API void lua_close (lua_State *L) { + L = G(L)->mainthread; /* only the main thread can be closed */ + lua_lock(L); + luaF_close(L, L->stack); /* close all upvalues for this thread */ + luaC_separateudata(L, 1); /* separate udata that have GC metamethods */ + L->errfunc = 0; /* no error function during GC metamethods */ + do { /* repeat until no more errors */ + L->ci = L->base_ci; + L->base = L->top = L->ci->base; + L->nCcalls = L->baseCcalls = 0; + } while (luaD_rawrunprotected(L, callallgcTM, NULL) != 0); + lua_assert(G(L)->tmudata == NULL); + luai_userstateclose(L); + close_state(L); +} + diff --git a/lib/lua/lstate.h b/lib/lua/lstate.h new file mode 100644 index 000000000000..3bc575b6bc8c --- /dev/null +++ b/lib/lua/lstate.h @@ -0,0 +1,169 @@ +/* +** $Id: lstate.h,v 2.24.1.2 2008/01/03 15:20:39 roberto Exp $ +** Global State +** See Copyright Notice in lua.h +*/ + +#ifndef lstate_h +#define lstate_h + +#include "lua.h" + +#include "lobject.h" +#include "ltm.h" +#include "lzio.h" + + + +struct lua_longjmp; /* defined in ldo.c */ + + +/* table of globals */ +#define gt(L) (&L->l_gt) + +/* registry */ +#define registry(L) (&G(L)->l_registry) + + +/* extra stack space to handle TM calls and some other extras */ +#define EXTRA_STACK 5 + + +#define BASIC_CI_SIZE 8 + +#define BASIC_STACK_SIZE (2*LUA_MINSTACK) + + + +typedef struct stringtable { + GCObject **hash; + lu_int32 nuse; /* number of elements */ + int size; +} stringtable; + + +/* +** informations about a call +*/ +typedef struct CallInfo { + StkId base; /* base for this function */ + StkId func; /* function index in the stack */ + StkId top; /* top for this function */ + const Instruction *savedpc; + int nresults; /* expected number of results from this function */ + int tailcalls; /* number of tail calls lost under this entry */ +} CallInfo; + + + +#define curr_func(L) (clvalue(L->ci->func)) +#define ci_func(ci) (clvalue((ci)->func)) +#define f_isLua(ci) (!ci_func(ci)->c.isC) +#define isLua(ci) (ttisfunction((ci)->func) && f_isLua(ci)) + + +/* +** `global state', shared by all threads of this state +*/ +typedef struct global_State { + stringtable strt; /* hash table for strings */ + lua_Alloc frealloc; /* function to reallocate memory */ + void *ud; /* auxiliary data to `frealloc' */ + lu_byte currentwhite; + lu_byte gcstate; /* state of garbage collector */ + int sweepstrgc; /* position of sweep in `strt' */ + GCObject *rootgc; /* list of all collectable objects */ + GCObject **sweepgc; /* position of sweep in `rootgc' */ + GCObject *gray; /* list of gray objects */ + GCObject *grayagain; /* list of objects to be traversed atomically */ + GCObject *weak; /* list of weak tables (to be cleared) */ + GCObject *tmudata; /* last element of list of userdata to be GC */ + Mbuffer buff; /* temporary buffer for string concatentation */ + lu_mem GCthreshold; + lu_mem totalbytes; /* number of bytes currently allocated */ + lu_mem estimate; /* an estimate of number of bytes actually in use */ + lu_mem gcdept; /* how much GC is `behind schedule' */ + int gcpause; /* size of pause between successive GCs */ + int gcstepmul; /* GC `granularity' */ + lua_CFunction panic; /* to be called in unprotected errors */ + TValue l_registry; + struct lua_State *mainthread; + UpVal uvhead; /* head of double-linked list of all open upvalues */ + struct Table *mt[NUM_TAGS]; /* metatables for basic types */ + TString *tmname[TM_N]; /* array with tag-method names */ +} global_State; + + +/* +** `per thread' state +*/ +struct lua_State { + CommonHeader; + lu_byte status; + StkId top; /* first free slot in the stack */ + StkId base; /* base of current function */ + global_State *l_G; + CallInfo *ci; /* call info for current function */ + const Instruction *savedpc; /* `savedpc' of current function */ + StkId stack_last; /* last free slot in the stack */ + StkId stack; /* stack base */ + CallInfo *end_ci; /* points after end of ci array*/ + CallInfo *base_ci; /* array of CallInfo's */ + int stacksize; + int size_ci; /* size of array `base_ci' */ + unsigned short nCcalls; /* number of nested C calls */ + unsigned short baseCcalls; /* nested C calls when resuming coroutine */ + lu_byte hookmask; + lu_byte allowhook; + int basehookcount; + int hookcount; + lua_Hook hook; + TValue l_gt; /* table of globals */ + TValue env; /* temporary place for environments */ + GCObject *openupval; /* list of open upvalues in this stack */ + GCObject *gclist; + struct lua_longjmp *errorJmp; /* current error recover point */ + ptrdiff_t errfunc; /* current error handling function (stack index) */ +}; + + +#define G(L) (L->l_G) + + +/* +** Union of all collectable objects +*/ +union GCObject { + GCheader gch; + union TString ts; + union Udata u; + union Closure cl; + struct Table h; + struct Proto p; + struct UpVal uv; + struct lua_State th; /* thread */ +}; + + +/* macros to convert a GCObject into a specific value */ +#define rawgco2ts(o) check_exp((o)->gch.tt == LUA_TSTRING, &((o)->ts)) +#define gco2ts(o) (&rawgco2ts(o)->tsv) +#define rawgco2u(o) check_exp((o)->gch.tt == LUA_TUSERDATA, &((o)->u)) +#define gco2u(o) (&rawgco2u(o)->uv) +#define gco2cl(o) check_exp((o)->gch.tt == LUA_TFUNCTION, &((o)->cl)) +#define gco2h(o) check_exp((o)->gch.tt == LUA_TTABLE, &((o)->h)) +#define gco2p(o) check_exp((o)->gch.tt == LUA_TPROTO, &((o)->p)) +#define gco2uv(o) check_exp((o)->gch.tt == LUA_TUPVAL, &((o)->uv)) +#define ngcotouv(o) \ + check_exp((o) == NULL || (o)->gch.tt == LUA_TUPVAL, &((o)->uv)) +#define gco2th(o) check_exp((o)->gch.tt == LUA_TTHREAD, &((o)->th)) + +/* macro to convert any Lua object into a GCObject */ +#define obj2gco(v) (cast(GCObject *, (v))) + + +LUAI_FUNC lua_State *luaE_newthread (lua_State *L); +LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1); + +#endif + diff --git a/lib/lua/lstring.c b/lib/lua/lstring.c new file mode 100644 index 000000000000..49113151cc70 --- /dev/null +++ b/lib/lua/lstring.c @@ -0,0 +1,111 @@ +/* +** $Id: lstring.c,v 2.8.1.1 2007/12/27 13:02:25 roberto Exp $ +** String table (keeps all strings handled by Lua) +** See Copyright Notice in lua.h +*/ + + +#include + +#define lstring_c +#define LUA_CORE + +#include "lua.h" + +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" + + + +void luaS_resize (lua_State *L, int newsize) { + GCObject **newhash; + stringtable *tb; + int i; + if (G(L)->gcstate == GCSsweepstring) + return; /* cannot resize during GC traverse */ + newhash = luaM_newvector(L, newsize, GCObject *); + tb = &G(L)->strt; + for (i=0; isize; i++) { + GCObject *p = tb->hash[i]; + while (p) { /* for each node in the list */ + GCObject *next = p->gch.next; /* save next */ + unsigned int h = gco2ts(p)->hash; + int h1 = lmod(h, newsize); /* new position */ + lua_assert(cast_int(h%newsize) == lmod(h, newsize)); + p->gch.next = newhash[h1]; /* chain it */ + newhash[h1] = p; + p = next; + } + } + luaM_freearray(L, tb->hash, tb->size, TString *); + tb->size = newsize; + tb->hash = newhash; +} + + +static TString *newlstr (lua_State *L, const char *str, size_t l, + unsigned int h) { + TString *ts; + stringtable *tb; + if (l+1 > (MAX_SIZET - sizeof(TString))/sizeof(char)) + luaM_toobig(L); + ts = cast(TString *, luaM_malloc(L, (l+1)*sizeof(char)+sizeof(TString))); + ts->tsv.len = l; + ts->tsv.hash = h; + ts->tsv.marked = luaC_white(G(L)); + ts->tsv.tt = LUA_TSTRING; + ts->tsv.reserved = 0; + memcpy(ts+1, str, l*sizeof(char)); + ((char *)(ts+1))[l] = '\0'; /* ending 0 */ + tb = &G(L)->strt; + h = lmod(h, tb->size); + ts->tsv.next = tb->hash[h]; /* chain new entry */ + tb->hash[h] = obj2gco(ts); + tb->nuse++; + if (tb->nuse > cast(lu_int32, tb->size) && tb->size <= MAX_INT/2) + luaS_resize(L, tb->size*2); /* too crowded */ + return ts; +} + + +TString *luaS_newlstr (lua_State *L, const char *str, size_t l) { + GCObject *o; + unsigned int h = cast(unsigned int, l); /* seed */ + size_t step = (l>>5)+1; /* if string is too long, don't hash all its chars */ + size_t l1; + for (l1=l; l1>=step; l1-=step) /* compute hash */ + h = h ^ ((h<<5)+(h>>2)+cast(unsigned char, str[l1-1])); + for (o = G(L)->strt.hash[lmod(h, G(L)->strt.size)]; + o != NULL; + o = o->gch.next) { + TString *ts = rawgco2ts(o); + if (ts->tsv.len == l && (memcmp(str, getstr(ts), l) == 0)) { + /* string may be dead */ + if (isdead(G(L), o)) changewhite(o); + return ts; + } + } + return newlstr(L, str, l, h); /* not found */ +} + + +Udata *luaS_newudata (lua_State *L, size_t s, Table *e) { + Udata *u; + if (s > MAX_SIZET - sizeof(Udata)) + luaM_toobig(L); + u = cast(Udata *, luaM_malloc(L, s + sizeof(Udata))); + u->uv.marked = luaC_white(G(L)); /* is not finalized */ + u->uv.tt = LUA_TUSERDATA; + u->uv.len = s; + u->uv.metatable = NULL; + u->uv.env = e; + /* chain it on udata list (after main thread) */ + u->uv.next = G(L)->mainthread->next; + G(L)->mainthread->next = obj2gco(u); + return u; +} + diff --git a/lib/lua/lstring.h b/lib/lua/lstring.h new file mode 100644 index 000000000000..73a2ff8b380d --- /dev/null +++ b/lib/lua/lstring.h @@ -0,0 +1,31 @@ +/* +** $Id: lstring.h,v 1.43.1.1 2007/12/27 13:02:25 roberto Exp $ +** String table (keep all strings handled by Lua) +** See Copyright Notice in lua.h +*/ + +#ifndef lstring_h +#define lstring_h + + +#include "lgc.h" +#include "lobject.h" +#include "lstate.h" + + +#define sizestring(s) (sizeof(union TString)+((s)->len+1)*sizeof(char)) + +#define sizeudata(u) (sizeof(union Udata)+(u)->len) + +#define luaS_new(L, s) (luaS_newlstr(L, s, strlen(s))) +#define luaS_newliteral(L, s) (luaS_newlstr(L, "" s, \ + (sizeof(s)/sizeof(char))-1)) + +#define luaS_fix(s) l_setbit((s)->tsv.marked, FIXEDBIT) + +LUAI_FUNC void luaS_resize (lua_State *L, int newsize); +LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, Table *e); +LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l); + + +#endif diff --git a/lib/lua/lstrlib.c b/lib/lua/lstrlib.c new file mode 100644 index 000000000000..7a03489bebd4 --- /dev/null +++ b/lib/lua/lstrlib.c @@ -0,0 +1,871 @@ +/* +** $Id: lstrlib.c,v 1.132.1.5 2010/05/14 15:34:19 roberto Exp $ +** Standard library for string operations and pattern-matching +** See Copyright Notice in lua.h +*/ + + +#include +#include +#include +#include +#include + +#define lstrlib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +/* macro to `unsign' a character */ +#define uchar(c) ((unsigned char)(c)) + + + +static int str_len (lua_State *L) { + size_t l; + luaL_checklstring(L, 1, &l); + lua_pushinteger(L, l); + return 1; +} + + +static ptrdiff_t posrelat (ptrdiff_t pos, size_t len) { + /* relative string position: negative means back from end */ + if (pos < 0) pos += (ptrdiff_t)len + 1; + return (pos >= 0) ? pos : 0; +} + + +static int str_sub (lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + ptrdiff_t start = posrelat(luaL_checkinteger(L, 2), l); + ptrdiff_t end = posrelat(luaL_optinteger(L, 3, -1), l); + if (start < 1) start = 1; + if (end > (ptrdiff_t)l) end = (ptrdiff_t)l; + if (start <= end) + lua_pushlstring(L, s+start-1, end-start+1); + else lua_pushliteral(L, ""); + return 1; +} + + +static int str_reverse (lua_State *L) { + size_t l; + luaL_Buffer b; + const char *s = luaL_checklstring(L, 1, &l); + luaL_buffinit(L, &b); + while (l--) luaL_addchar(&b, s[l]); + luaL_pushresult(&b); + return 1; +} + + +static int str_lower (lua_State *L) { + size_t l; + size_t i; + luaL_Buffer b; + const char *s = luaL_checklstring(L, 1, &l); + luaL_buffinit(L, &b); + for (i=0; i 0) + luaL_addlstring(&b, s, l); + luaL_pushresult(&b); + return 1; +} + + +static int str_byte (lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + ptrdiff_t posi = posrelat(luaL_optinteger(L, 2, 1), l); + ptrdiff_t pose = posrelat(luaL_optinteger(L, 3, posi), l); + int n, i; + if (posi <= 0) posi = 1; + if ((size_t)pose > l) pose = l; + if (posi > pose) return 0; /* empty interval; return no values */ + n = (int)(pose - posi + 1); + if (posi + n <= pose) /* overflow? */ + luaL_error(L, "string slice too long"); + luaL_checkstack(L, n, "string slice too long"); + for (i=0; i= ms->level || ms->capture[l].len == CAP_UNFINISHED) + return luaL_error(ms->L, "invalid capture index"); + return l; +} + + +static int capture_to_close (MatchState *ms) { + int level = ms->level; + for (level--; level>=0; level--) + if (ms->capture[level].len == CAP_UNFINISHED) return level; + return luaL_error(ms->L, "invalid pattern capture"); +} + + +static const char *classend (MatchState *ms, const char *p) { + switch (*p++) { + case L_ESC: { + if (*p == '\0') + luaL_error(ms->L, "malformed pattern (ends with " LUA_QL("%%") ")"); + return p+1; + } + case '[': { + if (*p == '^') p++; + do { /* look for a `]' */ + if (*p == '\0') + luaL_error(ms->L, "malformed pattern (missing " LUA_QL("]") ")"); + if (*(p++) == L_ESC && *p != '\0') + p++; /* skip escapes (e.g. `%]') */ + } while (*p != ']'); + return p+1; + } + default: { + return p; + } + } +} + + +static int match_class (int c, int cl) { + int res; + switch (tolower(cl)) { + case 'a' : res = isalpha(c); break; + case 'c' : res = iscntrl(c); break; + case 'd' : res = isdigit(c); break; + case 'l' : res = islower(c); break; + case 'p' : res = ispunct(c); break; + case 's' : res = isspace(c); break; + case 'u' : res = isupper(c); break; + case 'w' : res = isalnum(c); break; + case 'x' : res = isxdigit(c); break; + case 'z' : res = (c == 0); break; + default: return (cl == c); + } + return (islower(cl) ? res : !res); +} + + +static int matchbracketclass (int c, const char *p, const char *ec) { + int sig = 1; + if (*(p+1) == '^') { + sig = 0; + p++; /* skip the `^' */ + } + while (++p < ec) { + if (*p == L_ESC) { + p++; + if (match_class(c, uchar(*p))) + return sig; + } + else if ((*(p+1) == '-') && (p+2 < ec)) { + p+=2; + if (uchar(*(p-2)) <= c && c <= uchar(*p)) + return sig; + } + else if (uchar(*p) == c) return sig; + } + return !sig; +} + + +static int singlematch (int c, const char *p, const char *ep) { + switch (*p) { + case '.': return 1; /* matches any char */ + case L_ESC: return match_class(c, uchar(*(p+1))); + case '[': return matchbracketclass(c, p, ep-1); + default: return (uchar(*p) == c); + } +} + + +static const char *match (MatchState *ms, const char *s, const char *p); + + +static const char *matchbalance (MatchState *ms, const char *s, + const char *p) { + if (*p == 0 || *(p+1) == 0) + luaL_error(ms->L, "unbalanced pattern"); + if (*s != *p) return NULL; + else { + int b = *p; + int e = *(p+1); + int cont = 1; + while (++s < ms->src_end) { + if (*s == e) { + if (--cont == 0) return s+1; + } + else if (*s == b) cont++; + } + } + return NULL; /* string ends out of balance */ +} + + +static const char *max_expand (MatchState *ms, const char *s, + const char *p, const char *ep) { + ptrdiff_t i = 0; /* counts maximum expand for item */ + while ((s+i)src_end && singlematch(uchar(*(s+i)), p, ep)) + i++; + /* keeps trying to match with the maximum repetitions */ + while (i>=0) { + const char *res = match(ms, (s+i), ep+1); + if (res) return res; + i--; /* else didn't match; reduce 1 repetition to try again */ + } + return NULL; +} + + +static const char *min_expand (MatchState *ms, const char *s, + const char *p, const char *ep) { + for (;;) { + const char *res = match(ms, s, ep+1); + if (res != NULL) + return res; + else if (ssrc_end && singlematch(uchar(*s), p, ep)) + s++; /* try with one more repetition */ + else return NULL; + } +} + + +static const char *start_capture (MatchState *ms, const char *s, + const char *p, int what) { + const char *res; + int level = ms->level; + if (level >= LUA_MAXCAPTURES) luaL_error(ms->L, "too many captures"); + ms->capture[level].init = s; + ms->capture[level].len = what; + ms->level = level+1; + if ((res=match(ms, s, p)) == NULL) /* match failed? */ + ms->level--; /* undo capture */ + return res; +} + + +static const char *end_capture (MatchState *ms, const char *s, + const char *p) { + int l = capture_to_close(ms); + const char *res; + ms->capture[l].len = s - ms->capture[l].init; /* close capture */ + if ((res = match(ms, s, p)) == NULL) /* match failed? */ + ms->capture[l].len = CAP_UNFINISHED; /* undo capture */ + return res; +} + + +static const char *match_capture (MatchState *ms, const char *s, int l) { + size_t len; + l = check_capture(ms, l); + len = ms->capture[l].len; + if ((size_t)(ms->src_end-s) >= len && + memcmp(ms->capture[l].init, s, len) == 0) + return s+len; + else return NULL; +} + + +static const char *match (MatchState *ms, const char *s, const char *p) { + init: /* using goto's to optimize tail recursion */ + switch (*p) { + case '(': { /* start capture */ + if (*(p+1) == ')') /* position capture? */ + return start_capture(ms, s, p+2, CAP_POSITION); + else + return start_capture(ms, s, p+1, CAP_UNFINISHED); + } + case ')': { /* end capture */ + return end_capture(ms, s, p+1); + } + case L_ESC: { + switch (*(p+1)) { + case 'b': { /* balanced string? */ + s = matchbalance(ms, s, p+2); + if (s == NULL) return NULL; + p+=4; goto init; /* else return match(ms, s, p+4); */ + } + case 'f': { /* frontier? */ + const char *ep; char previous; + p += 2; + if (*p != '[') + luaL_error(ms->L, "missing " LUA_QL("[") " after " + LUA_QL("%%f") " in pattern"); + ep = classend(ms, p); /* points to what is next */ + previous = (s == ms->src_init) ? '\0' : *(s-1); + if (matchbracketclass(uchar(previous), p, ep-1) || + !matchbracketclass(uchar(*s), p, ep-1)) return NULL; + p=ep; goto init; /* else return match(ms, s, ep); */ + } + default: { + if (isdigit(uchar(*(p+1)))) { /* capture results (%0-%9)? */ + s = match_capture(ms, s, uchar(*(p+1))); + if (s == NULL) return NULL; + p+=2; goto init; /* else return match(ms, s, p+2) */ + } + goto dflt; /* case default */ + } + } + } + case '\0': { /* end of pattern */ + return s; /* match succeeded */ + } + case '$': { + if (*(p+1) == '\0') /* is the `$' the last char in pattern? */ + return (s == ms->src_end) ? s : NULL; /* check end of string */ + else goto dflt; + } + default: dflt: { /* it is a pattern item */ + const char *ep = classend(ms, p); /* points to what is next */ + int m = ssrc_end && singlematch(uchar(*s), p, ep); + switch (*ep) { + case '?': { /* optional */ + const char *res; + if (m && ((res=match(ms, s+1, ep+1)) != NULL)) + return res; + p=ep+1; goto init; /* else return match(ms, s, ep+1); */ + } + case '*': { /* 0 or more repetitions */ + return max_expand(ms, s, p, ep); + } + case '+': { /* 1 or more repetitions */ + return (m ? max_expand(ms, s+1, p, ep) : NULL); + } + case '-': { /* 0 or more repetitions (minimum) */ + return min_expand(ms, s, p, ep); + } + default: { + if (!m) return NULL; + s++; p=ep; goto init; /* else return match(ms, s+1, ep); */ + } + } + } + } +} + + + +static const char *lmemfind (const char *s1, size_t l1, + const char *s2, size_t l2) { + if (l2 == 0) return s1; /* empty strings are everywhere */ + else if (l2 > l1) return NULL; /* avoids a negative `l1' */ + else { + const char *init; /* to search for a `*s2' inside `s1' */ + l2--; /* 1st char will be checked by `memchr' */ + l1 = l1-l2; /* `s2' cannot be found after that */ + while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) { + init++; /* 1st char is already checked */ + if (memcmp(init, s2+1, l2) == 0) + return init-1; + else { /* correct `l1' and `s1' to try again */ + l1 -= init-s1; + s1 = init; + } + } + return NULL; /* not found */ + } +} + + +static void push_onecapture (MatchState *ms, int i, const char *s, + const char *e) { + if (i >= ms->level) { + if (i == 0) /* ms->level == 0, too */ + lua_pushlstring(ms->L, s, e - s); /* add whole match */ + else + luaL_error(ms->L, "invalid capture index"); + } + else { + ptrdiff_t l = ms->capture[i].len; + if (l == CAP_UNFINISHED) luaL_error(ms->L, "unfinished capture"); + if (l == CAP_POSITION) + lua_pushinteger(ms->L, ms->capture[i].init - ms->src_init + 1); + else + lua_pushlstring(ms->L, ms->capture[i].init, l); + } +} + + +static int push_captures (MatchState *ms, const char *s, const char *e) { + int i; + int nlevels = (ms->level == 0 && s) ? 1 : ms->level; + luaL_checkstack(ms->L, nlevels, "too many captures"); + for (i = 0; i < nlevels; i++) + push_onecapture(ms, i, s, e); + return nlevels; /* number of strings pushed */ +} + + +static int str_find_aux (lua_State *L, int find) { + size_t l1, l2; + const char *s = luaL_checklstring(L, 1, &l1); + const char *p = luaL_checklstring(L, 2, &l2); + ptrdiff_t init = posrelat(luaL_optinteger(L, 3, 1), l1) - 1; + if (init < 0) init = 0; + else if ((size_t)(init) > l1) init = (ptrdiff_t)l1; + if (find && (lua_toboolean(L, 4) || /* explicit request? */ + strpbrk(p, SPECIALS) == NULL)) { /* or no special characters? */ + /* do a plain search */ + const char *s2 = lmemfind(s+init, l1-init, p, l2); + if (s2) { + lua_pushinteger(L, s2-s+1); + lua_pushinteger(L, s2-s+l2); + return 2; + } + } + else { + MatchState ms; + int anchor = (*p == '^') ? (p++, 1) : 0; + const char *s1=s+init; + ms.L = L; + ms.src_init = s; + ms.src_end = s+l1; + do { + const char *res; + ms.level = 0; + if ((res=match(&ms, s1, p)) != NULL) { + if (find) { + lua_pushinteger(L, s1-s+1); /* start */ + lua_pushinteger(L, res-s); /* end */ + return push_captures(&ms, NULL, 0) + 2; + } + else + return push_captures(&ms, s1, res); + } + } while (s1++ < ms.src_end && !anchor); + } + lua_pushnil(L); /* not found */ + return 1; +} + + +static int str_find (lua_State *L) { + return str_find_aux(L, 1); +} + + +static int str_match (lua_State *L) { + return str_find_aux(L, 0); +} + + +static int gmatch_aux (lua_State *L) { + MatchState ms; + size_t ls; + const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls); + const char *p = lua_tostring(L, lua_upvalueindex(2)); + const char *src; + ms.L = L; + ms.src_init = s; + ms.src_end = s+ls; + for (src = s + (size_t)lua_tointeger(L, lua_upvalueindex(3)); + src <= ms.src_end; + src++) { + const char *e; + ms.level = 0; + if ((e = match(&ms, src, p)) != NULL) { + lua_Integer newstart = e-s; + if (e == src) newstart++; /* empty match? go at least one position */ + lua_pushinteger(L, newstart); + lua_replace(L, lua_upvalueindex(3)); + return push_captures(&ms, src, e); + } + } + return 0; /* not found */ +} + + +static int gmatch (lua_State *L) { + luaL_checkstring(L, 1); + luaL_checkstring(L, 2); + lua_settop(L, 2); + lua_pushinteger(L, 0); + lua_pushcclosure(L, gmatch_aux, 3); + return 1; +} + + +static int gfind_nodef (lua_State *L) { + return luaL_error(L, LUA_QL("string.gfind") " was renamed to " + LUA_QL("string.gmatch")); +} + + +static void add_s (MatchState *ms, luaL_Buffer *b, const char *s, + const char *e) { + size_t l, i; + const char *news = lua_tolstring(ms->L, 3, &l); + for (i = 0; i < l; i++) { + if (news[i] != L_ESC) + luaL_addchar(b, news[i]); + else { + i++; /* skip ESC */ + if (!isdigit(uchar(news[i]))) + luaL_addchar(b, news[i]); + else if (news[i] == '0') + luaL_addlstring(b, s, e - s); + else { + push_onecapture(ms, news[i] - '1', s, e); + luaL_addvalue(b); /* add capture to accumulated result */ + } + } + } +} + + +static void add_value (MatchState *ms, luaL_Buffer *b, const char *s, + const char *e) { + lua_State *L = ms->L; + switch (lua_type(L, 3)) { + case LUA_TNUMBER: + case LUA_TSTRING: { + add_s(ms, b, s, e); + return; + } + case LUA_TFUNCTION: { + int n; + lua_pushvalue(L, 3); + n = push_captures(ms, s, e); + lua_call(L, n, 1); + break; + } + case LUA_TTABLE: { + push_onecapture(ms, 0, s, e); + lua_gettable(L, 3); + break; + } + } + if (!lua_toboolean(L, -1)) { /* nil or false? */ + lua_pop(L, 1); + lua_pushlstring(L, s, e - s); /* keep original text */ + } + else if (!lua_isstring(L, -1)) + luaL_error(L, "invalid replacement value (a %s)", luaL_typename(L, -1)); + luaL_addvalue(b); /* add result to accumulator */ +} + + +static int str_gsub (lua_State *L) { + size_t srcl; + const char *src = luaL_checklstring(L, 1, &srcl); + const char *p = luaL_checkstring(L, 2); + int tr = lua_type(L, 3); + int max_s = luaL_optint(L, 4, srcl+1); + int anchor = (*p == '^') ? (p++, 1) : 0; + int n = 0; + MatchState ms; + luaL_Buffer b; + luaL_argcheck(L, tr == LUA_TNUMBER || tr == LUA_TSTRING || + tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3, + "string/function/table expected"); + luaL_buffinit(L, &b); + ms.L = L; + ms.src_init = src; + ms.src_end = src+srcl; + while (n < max_s) { + const char *e; + ms.level = 0; + e = match(&ms, src, p); + if (e) { + n++; + add_value(&ms, &b, src, e); + } + if (e && e>src) /* non empty match? */ + src = e; /* skip it */ + else if (src < ms.src_end) + luaL_addchar(&b, *src++); + else break; + if (anchor) break; + } + luaL_addlstring(&b, src, ms.src_end-src); + luaL_pushresult(&b); + lua_pushinteger(L, n); /* number of substitutions */ + return 2; +} + +/* }====================================================== */ + + +/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */ +#define MAX_ITEM 512 +/* valid flags in a format specification */ +#define FLAGS "-+ #0" +/* +** maximum size of each format specification (such as '%-099.99d') +** (+10 accounts for %99.99x plus margin of error) +*/ +#define MAX_FORMAT (sizeof(FLAGS) + sizeof(LUA_INTFRMLEN) + 10) + + +static void addquoted (lua_State *L, luaL_Buffer *b, int arg) { + size_t l; + const char *s = luaL_checklstring(L, arg, &l); + luaL_addchar(b, '"'); + while (l--) { + switch (*s) { + case '"': case '\\': case '\n': { + luaL_addchar(b, '\\'); + luaL_addchar(b, *s); + break; + } + case '\r': { + luaL_addlstring(b, "\\r", 2); + break; + } + case '\0': { + luaL_addlstring(b, "\\000", 4); + break; + } + default: { + luaL_addchar(b, *s); + break; + } + } + s++; + } + luaL_addchar(b, '"'); +} + +static const char *scanformat (lua_State *L, const char *strfrmt, char *form) { + const char *p = strfrmt; + while (*p != '\0' && strchr(FLAGS, *p) != NULL) p++; /* skip flags */ + if ((size_t)(p - strfrmt) >= sizeof(FLAGS)) + luaL_error(L, "invalid format (repeated flags)"); + if (isdigit(uchar(*p))) p++; /* skip width */ + if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ + if (*p == '.') { + p++; + if (isdigit(uchar(*p))) p++; /* skip precision */ + if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ + } + if (isdigit(uchar(*p))) + luaL_error(L, "invalid format (width or precision too long)"); + *(form++) = '%'; + strncpy(form, strfrmt, p - strfrmt + 1); + form += p - strfrmt + 1; + *form = '\0'; + return p; +} + + +static void addintlen (char *form) { + size_t l = strlen(form); + char spec = form[l - 1]; + strcpy(form + l - 1, LUA_INTFRMLEN); + form[l + sizeof(LUA_INTFRMLEN) - 2] = spec; + form[l + sizeof(LUA_INTFRMLEN) - 1] = '\0'; +} + + +static int str_format (lua_State *L) { + int top = lua_gettop(L); + int arg = 1; + size_t sfl; + const char *strfrmt = luaL_checklstring(L, arg, &sfl); + const char *strfrmt_end = strfrmt+sfl; + luaL_Buffer b; + luaL_buffinit(L, &b); + while (strfrmt < strfrmt_end) { + if (*strfrmt != L_ESC) + luaL_addchar(&b, *strfrmt++); + else if (*++strfrmt == L_ESC) + luaL_addchar(&b, *strfrmt++); /* %% */ + else { /* format item */ + char form[MAX_FORMAT]; /* to store the format (`%...') */ + char buff[MAX_ITEM]; /* to store the formatted item */ + if (++arg > top) + luaL_argerror(L, arg, "no value"); + strfrmt = scanformat(L, strfrmt, form); + switch (*strfrmt++) { + case 'c': { + sprintf(buff, form, (int)luaL_checknumber(L, arg)); + break; + } + case 'd': case 'i': { + addintlen(form); + sprintf(buff, form, (LUA_INTFRM_T)luaL_checknumber(L, arg)); + break; + } + case 'o': case 'u': case 'x': case 'X': { + addintlen(form); + sprintf(buff, form, (unsigned LUA_INTFRM_T)luaL_checknumber(L, arg)); + break; + } + case 'e': case 'E': case 'f': + case 'g': case 'G': { + sprintf(buff, form, (double)luaL_checknumber(L, arg)); + break; + } + case 'q': { + addquoted(L, &b, arg); + continue; /* skip the 'addsize' at the end */ + } + case 's': { + size_t l; + const char *s = luaL_checklstring(L, arg, &l); + if (!strchr(form, '.') && l >= 100) { + /* no precision and string is too long to be formatted; + keep original string */ + lua_pushvalue(L, arg); + luaL_addvalue(&b); + continue; /* skip the `addsize' at the end */ + } + else { + sprintf(buff, form, s); + break; + } + } + default: { /* also treat cases `pnLlh' */ + return luaL_error(L, "invalid option " LUA_QL("%%%c") " to " + LUA_QL("format"), *(strfrmt - 1)); + } + } + luaL_addlstring(&b, buff, strlen(buff)); + } + } + luaL_pushresult(&b); + return 1; +} + + +static const luaL_Reg strlib[] = { + {"byte", str_byte}, + {"char", str_char}, + {"dump", str_dump}, + {"find", str_find}, + {"format", str_format}, + {"gfind", gfind_nodef}, + {"gmatch", gmatch}, + {"gsub", str_gsub}, + {"len", str_len}, + {"lower", str_lower}, + {"match", str_match}, + {"rep", str_rep}, + {"reverse", str_reverse}, + {"sub", str_sub}, + {"upper", str_upper}, + {NULL, NULL} +}; + + +static void createmetatable (lua_State *L) { + lua_createtable(L, 0, 1); /* create metatable for strings */ + lua_pushliteral(L, ""); /* dummy string */ + lua_pushvalue(L, -2); + lua_setmetatable(L, -2); /* set string metatable */ + lua_pop(L, 1); /* pop dummy string */ + lua_pushvalue(L, -2); /* string library... */ + lua_setfield(L, -2, "__index"); /* ...is the __index metamethod */ + lua_pop(L, 1); /* pop metatable */ +} + + +/* +** Open string library +*/ +LUALIB_API int luaopen_string (lua_State *L) { + luaL_register(L, LUA_STRLIBNAME, strlib); +#if defined(LUA_COMPAT_GFIND) + lua_getfield(L, -1, "gmatch"); + lua_setfield(L, -2, "gfind"); +#endif + createmetatable(L); + return 1; +} + diff --git a/lib/lua/ltable.c b/lib/lua/ltable.c new file mode 100644 index 000000000000..ec84f4fabc51 --- /dev/null +++ b/lib/lua/ltable.c @@ -0,0 +1,588 @@ +/* +** $Id: ltable.c,v 2.32.1.2 2007/12/28 15:32:23 roberto Exp $ +** Lua tables (hash) +** See Copyright Notice in lua.h +*/ + + +/* +** Implementation of tables (aka arrays, objects, or hash tables). +** Tables keep its elements in two parts: an array part and a hash part. +** Non-negative integer keys are all candidates to be kept in the array +** part. The actual size of the array is the largest `n' such that at +** least half the slots between 0 and n are in use. +** Hash uses a mix of chained scatter table with Brent's variation. +** A main invariant of these tables is that, if an element is not +** in its main position (i.e. the `original' position that its hash gives +** to it), then the colliding element is in its own main position. +** Hence even when the load factor reaches 100%, performance remains good. +*/ + +#include +#include + +#define ltable_c +#define LUA_CORE + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "ltable.h" + + +/* +** max size of array part is 2^MAXBITS +*/ +#if LUAI_BITSINT > 26 +#define MAXBITS 26 +#else +#define MAXBITS (LUAI_BITSINT-2) +#endif + +#define MAXASIZE (1 << MAXBITS) + + +#define hashpow2(t,n) (gnode(t, lmod((n), sizenode(t)))) + +#define hashstr(t,str) hashpow2(t, (str)->tsv.hash) +#define hashboolean(t,p) hashpow2(t, p) + + +/* +** for some types, it is better to avoid modulus by power of 2, as +** they tend to have many 2 factors. +*/ +#define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1)))) + + +#define hashpointer(t,p) hashmod(t, IntPoint(p)) + + +/* +** number of ints inside a lua_Number +*/ +#define numints cast_int(sizeof(lua_Number)/sizeof(int)) + + + +#define dummynode (&dummynode_) + +static const Node dummynode_ = { + {{NULL}, LUA_TNIL}, /* value */ + {{{NULL}, LUA_TNIL, NULL}} /* key */ +}; + + +/* +** hash for lua_Numbers +*/ +static Node *hashnum (const Table *t, lua_Number n) { + unsigned int a[numints]; + int i; + if (luai_numeq(n, 0)) /* avoid problems with -0 */ + return gnode(t, 0); + memcpy(a, &n, sizeof(a)); + for (i = 1; i < numints; i++) a[0] += a[i]; + return hashmod(t, a[0]); +} + + + +/* +** returns the `main' position of an element in a table (that is, the index +** of its hash value) +*/ +static Node *mainposition (const Table *t, const TValue *key) { + switch (ttype(key)) { + case LUA_TNUMBER: + return hashnum(t, nvalue(key)); + case LUA_TSTRING: + return hashstr(t, rawtsvalue(key)); + case LUA_TBOOLEAN: + return hashboolean(t, bvalue(key)); + case LUA_TLIGHTUSERDATA: + return hashpointer(t, pvalue(key)); + default: + return hashpointer(t, gcvalue(key)); + } +} + + +/* +** returns the index for `key' if `key' is an appropriate key to live in +** the array part of the table, -1 otherwise. +*/ +static int arrayindex (const TValue *key) { + if (ttisnumber(key)) { + lua_Number n = nvalue(key); + int k; + lua_number2int(k, n); + if (luai_numeq(cast_num(k), n)) + return k; + } + return -1; /* `key' did not match some condition */ +} + + +/* +** returns the index of a `key' for table traversals. First goes all +** elements in the array part, then elements in the hash part. The +** beginning of a traversal is signalled by -1. +*/ +static int findindex (lua_State *L, Table *t, StkId key) { + int i; + if (ttisnil(key)) return -1; /* first iteration */ + i = arrayindex(key); + if (0 < i && i <= t->sizearray) /* is `key' inside array part? */ + return i-1; /* yes; that's the index (corrected to C) */ + else { + Node *n = mainposition(t, key); + do { /* check whether `key' is somewhere in the chain */ + /* key may be dead already, but it is ok to use it in `next' */ + if (luaO_rawequalObj(key2tval(n), key) || + (ttype(gkey(n)) == LUA_TDEADKEY && iscollectable(key) && + gcvalue(gkey(n)) == gcvalue(key))) { + i = cast_int(n - gnode(t, 0)); /* key index in hash table */ + /* hash elements are numbered after array ones */ + return i + t->sizearray; + } + else n = gnext(n); + } while (n); + luaG_runerror(L, "invalid key to " LUA_QL("next")); /* key not found */ + return 0; /* to avoid warnings */ + } +} + + +int luaH_next (lua_State *L, Table *t, StkId key) { + int i = findindex(L, t, key); /* find original element */ + for (i++; i < t->sizearray; i++) { /* try first array part */ + if (!ttisnil(&t->array[i])) { /* a non-nil value? */ + setnvalue(key, cast_num(i+1)); + setobj2s(L, key+1, &t->array[i]); + return 1; + } + } + for (i -= t->sizearray; i < sizenode(t); i++) { /* then hash part */ + if (!ttisnil(gval(gnode(t, i)))) { /* a non-nil value? */ + setobj2s(L, key, key2tval(gnode(t, i))); + setobj2s(L, key+1, gval(gnode(t, i))); + return 1; + } + } + return 0; /* no more elements */ +} + + +/* +** {============================================================= +** Rehash +** ============================================================== +*/ + + +static int computesizes (int nums[], int *narray) { + int i; + int twotoi; /* 2^i */ + int a = 0; /* number of elements smaller than 2^i */ + int na = 0; /* number of elements to go to array part */ + int n = 0; /* optimal size for array part */ + for (i = 0, twotoi = 1; twotoi/2 < *narray; i++, twotoi *= 2) { + if (nums[i] > 0) { + a += nums[i]; + if (a > twotoi/2) { /* more than half elements present? */ + n = twotoi; /* optimal size (till now) */ + na = a; /* all elements smaller than n will go to array part */ + } + } + if (a == *narray) break; /* all elements already counted */ + } + *narray = n; + lua_assert(*narray/2 <= na && na <= *narray); + return na; +} + + +static int countint (const TValue *key, int *nums) { + int k = arrayindex(key); + if (0 < k && k <= MAXASIZE) { /* is `key' an appropriate array index? */ + nums[ceillog2(k)]++; /* count as such */ + return 1; + } + else + return 0; +} + + +static int numusearray (const Table *t, int *nums) { + int lg; + int ttlg; /* 2^lg */ + int ause = 0; /* summation of `nums' */ + int i = 1; /* count to traverse all array keys */ + for (lg=0, ttlg=1; lg<=MAXBITS; lg++, ttlg*=2) { /* for each slice */ + int lc = 0; /* counter */ + int lim = ttlg; + if (lim > t->sizearray) { + lim = t->sizearray; /* adjust upper limit */ + if (i > lim) + break; /* no more elements to count */ + } + /* count elements in range (2^(lg-1), 2^lg] */ + for (; i <= lim; i++) { + if (!ttisnil(&t->array[i-1])) + lc++; + } + nums[lg] += lc; + ause += lc; + } + return ause; +} + + +static int numusehash (const Table *t, int *nums, int *pnasize) { + int totaluse = 0; /* total number of elements */ + int ause = 0; /* summation of `nums' */ + int i = sizenode(t); + while (i--) { + Node *n = &t->node[i]; + if (!ttisnil(gval(n))) { + ause += countint(key2tval(n), nums); + totaluse++; + } + } + *pnasize += ause; + return totaluse; +} + + +static void setarrayvector (lua_State *L, Table *t, int size) { + int i; + luaM_reallocvector(L, t->array, t->sizearray, size, TValue); + for (i=t->sizearray; iarray[i]); + t->sizearray = size; +} + + +static void setnodevector (lua_State *L, Table *t, int size) { + int lsize; + if (size == 0) { /* no elements to hash part? */ + t->node = cast(Node *, dummynode); /* use common `dummynode' */ + lsize = 0; + } + else { + int i; + lsize = ceillog2(size); + if (lsize > MAXBITS) + luaG_runerror(L, "table overflow"); + size = twoto(lsize); + t->node = luaM_newvector(L, size, Node); + for (i=0; ilsizenode = cast_byte(lsize); + t->lastfree = gnode(t, size); /* all positions are free */ +} + + +static void resize (lua_State *L, Table *t, int nasize, int nhsize) { + int i; + int oldasize = t->sizearray; + int oldhsize = t->lsizenode; + Node *nold = t->node; /* save old hash ... */ + if (nasize > oldasize) /* array part must grow? */ + setarrayvector(L, t, nasize); + /* create new hash part with appropriate size */ + setnodevector(L, t, nhsize); + if (nasize < oldasize) { /* array part must shrink? */ + t->sizearray = nasize; + /* re-insert elements from vanishing slice */ + for (i=nasize; iarray[i])) + setobjt2t(L, luaH_setnum(L, t, i+1), &t->array[i]); + } + /* shrink array */ + luaM_reallocvector(L, t->array, oldasize, nasize, TValue); + } + /* re-insert elements from hash part */ + for (i = twoto(oldhsize) - 1; i >= 0; i--) { + Node *old = nold+i; + if (!ttisnil(gval(old))) + setobjt2t(L, luaH_set(L, t, key2tval(old)), gval(old)); + } + if (nold != dummynode) + luaM_freearray(L, nold, twoto(oldhsize), Node); /* free old array */ +} + + +void luaH_resizearray (lua_State *L, Table *t, int nasize) { + int nsize = (t->node == dummynode) ? 0 : sizenode(t); + resize(L, t, nasize, nsize); +} + + +static void rehash (lua_State *L, Table *t, const TValue *ek) { + int nasize, na; + int nums[MAXBITS+1]; /* nums[i] = number of keys between 2^(i-1) and 2^i */ + int i; + int totaluse; + for (i=0; i<=MAXBITS; i++) nums[i] = 0; /* reset counts */ + nasize = numusearray(t, nums); /* count keys in array part */ + totaluse = nasize; /* all those keys are integer keys */ + totaluse += numusehash(t, nums, &nasize); /* count keys in hash part */ + /* count extra key */ + nasize += countint(ek, nums); + totaluse++; + /* compute new size for array part */ + na = computesizes(nums, &nasize); + /* resize the table to new computed sizes */ + resize(L, t, nasize, totaluse - na); +} + + + +/* +** }============================================================= +*/ + + +Table *luaH_new (lua_State *L, int narray, int nhash) { + Table *t = luaM_new(L, Table); + luaC_link(L, obj2gco(t), LUA_TTABLE); + t->metatable = NULL; + t->flags = cast_byte(~0); + /* temporary values (kept only if some malloc fails) */ + t->array = NULL; + t->sizearray = 0; + t->lsizenode = 0; + t->node = cast(Node *, dummynode); + setarrayvector(L, t, narray); + setnodevector(L, t, nhash); + return t; +} + + +void luaH_free (lua_State *L, Table *t) { + if (t->node != dummynode) + luaM_freearray(L, t->node, sizenode(t), Node); + luaM_freearray(L, t->array, t->sizearray, TValue); + luaM_free(L, t); +} + + +static Node *getfreepos (Table *t) { + while (t->lastfree-- > t->node) { + if (ttisnil(gkey(t->lastfree))) + return t->lastfree; + } + return NULL; /* could not find a free place */ +} + + + +/* +** inserts a new key into a hash table; first, check whether key's main +** position is free. If not, check whether colliding node is in its main +** position or not: if it is not, move colliding node to an empty place and +** put new key in its main position; otherwise (colliding node is in its main +** position), new key goes to an empty position. +*/ +static TValue *newkey (lua_State *L, Table *t, const TValue *key) { + Node *mp = mainposition(t, key); + if (!ttisnil(gval(mp)) || mp == dummynode) { + Node *othern; + Node *n = getfreepos(t); /* get a free place */ + if (n == NULL) { /* cannot find a free place? */ + rehash(L, t, key); /* grow table */ + return luaH_set(L, t, key); /* re-insert key into grown table */ + } + lua_assert(n != dummynode); + othern = mainposition(t, key2tval(mp)); + if (othern != mp) { /* is colliding node out of its main position? */ + /* yes; move colliding node into free position */ + while (gnext(othern) != mp) othern = gnext(othern); /* find previous */ + gnext(othern) = n; /* redo the chain with `n' in place of `mp' */ + *n = *mp; /* copy colliding node into free pos. (mp->next also goes) */ + gnext(mp) = NULL; /* now `mp' is free */ + setnilvalue(gval(mp)); + } + else { /* colliding node is in its own main position */ + /* new node will go into free position */ + gnext(n) = gnext(mp); /* chain new position */ + gnext(mp) = n; + mp = n; + } + } + gkey(mp)->value = key->value; gkey(mp)->tt = key->tt; + luaC_barriert(L, t, key); + lua_assert(ttisnil(gval(mp))); + return gval(mp); +} + + +/* +** search function for integers +*/ +const TValue *luaH_getnum (Table *t, int key) { + /* (1 <= key && key <= t->sizearray) */ + if (cast(unsigned int, key-1) < cast(unsigned int, t->sizearray)) + return &t->array[key-1]; + else { + lua_Number nk = cast_num(key); + Node *n = hashnum(t, nk); + do { /* check whether `key' is somewhere in the chain */ + if (ttisnumber(gkey(n)) && luai_numeq(nvalue(gkey(n)), nk)) + return gval(n); /* that's it */ + else n = gnext(n); + } while (n); + return luaO_nilobject; + } +} + + +/* +** search function for strings +*/ +const TValue *luaH_getstr (Table *t, TString *key) { + Node *n = hashstr(t, key); + do { /* check whether `key' is somewhere in the chain */ + if (ttisstring(gkey(n)) && rawtsvalue(gkey(n)) == key) + return gval(n); /* that's it */ + else n = gnext(n); + } while (n); + return luaO_nilobject; +} + + +/* +** main search function +*/ +const TValue *luaH_get (Table *t, const TValue *key) { + switch (ttype(key)) { + case LUA_TNIL: return luaO_nilobject; + case LUA_TSTRING: return luaH_getstr(t, rawtsvalue(key)); + case LUA_TNUMBER: { + int k; + lua_Number n = nvalue(key); + lua_number2int(k, n); + if (luai_numeq(cast_num(k), nvalue(key))) /* index is int? */ + return luaH_getnum(t, k); /* use specialized version */ + /* else go through */ + } + default: { + Node *n = mainposition(t, key); + do { /* check whether `key' is somewhere in the chain */ + if (luaO_rawequalObj(key2tval(n), key)) + return gval(n); /* that's it */ + else n = gnext(n); + } while (n); + return luaO_nilobject; + } + } +} + + +TValue *luaH_set (lua_State *L, Table *t, const TValue *key) { + const TValue *p = luaH_get(t, key); + t->flags = 0; + if (p != luaO_nilobject) + return cast(TValue *, p); + else { + if (ttisnil(key)) luaG_runerror(L, "table index is nil"); + else if (ttisnumber(key) && luai_numisnan(nvalue(key))) + luaG_runerror(L, "table index is NaN"); + return newkey(L, t, key); + } +} + + +TValue *luaH_setnum (lua_State *L, Table *t, int key) { + const TValue *p = luaH_getnum(t, key); + if (p != luaO_nilobject) + return cast(TValue *, p); + else { + TValue k; + setnvalue(&k, cast_num(key)); + return newkey(L, t, &k); + } +} + + +TValue *luaH_setstr (lua_State *L, Table *t, TString *key) { + const TValue *p = luaH_getstr(t, key); + if (p != luaO_nilobject) + return cast(TValue *, p); + else { + TValue k; + setsvalue(L, &k, key); + return newkey(L, t, &k); + } +} + + +static int unbound_search (Table *t, unsigned int j) { + unsigned int i = j; /* i is zero or a present index */ + j++; + /* find `i' and `j' such that i is present and j is not */ + while (!ttisnil(luaH_getnum(t, j))) { + i = j; + j *= 2; + if (j > cast(unsigned int, MAX_INT)) { /* overflow? */ + /* table was built with bad purposes: resort to linear search */ + i = 1; + while (!ttisnil(luaH_getnum(t, i))) i++; + return i - 1; + } + } + /* now do a binary search between them */ + while (j - i > 1) { + unsigned int m = (i+j)/2; + if (ttisnil(luaH_getnum(t, m))) j = m; + else i = m; + } + return i; +} + + +/* +** Try to find a boundary in table `t'. A `boundary' is an integer index +** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil). +*/ +int luaH_getn (Table *t) { + unsigned int j = t->sizearray; + if (j > 0 && ttisnil(&t->array[j - 1])) { + /* there is a boundary in the array part: (binary) search for it */ + unsigned int i = 0; + while (j - i > 1) { + unsigned int m = (i+j)/2; + if (ttisnil(&t->array[m - 1])) j = m; + else i = m; + } + return i; + } + /* else must find a boundary in hash part */ + else if (t->node == dummynode) /* hash part is empty? */ + return j; /* that is easy... */ + else return unbound_search(t, j); +} + + + +#if defined(LUA_DEBUG) + +Node *luaH_mainposition (const Table *t, const TValue *key) { + return mainposition(t, key); +} + +int luaH_isdummy (Node *n) { return n == dummynode; } + +#endif diff --git a/lib/lua/ltable.h b/lib/lua/ltable.h new file mode 100644 index 000000000000..f5b9d5ead0f4 --- /dev/null +++ b/lib/lua/ltable.h @@ -0,0 +1,40 @@ +/* +** $Id: ltable.h,v 2.10.1.1 2007/12/27 13:02:25 roberto Exp $ +** Lua tables (hash) +** See Copyright Notice in lua.h +*/ + +#ifndef ltable_h +#define ltable_h + +#include "lobject.h" + + +#define gnode(t,i) (&(t)->node[i]) +#define gkey(n) (&(n)->i_key.nk) +#define gval(n) (&(n)->i_val) +#define gnext(n) ((n)->i_key.nk.next) + +#define key2tval(n) (&(n)->i_key.tvk) + + +LUAI_FUNC const TValue *luaH_getnum (Table *t, int key); +LUAI_FUNC TValue *luaH_setnum (lua_State *L, Table *t, int key); +LUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key); +LUAI_FUNC TValue *luaH_setstr (lua_State *L, Table *t, TString *key); +LUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key); +LUAI_FUNC TValue *luaH_set (lua_State *L, Table *t, const TValue *key); +LUAI_FUNC Table *luaH_new (lua_State *L, int narray, int lnhash); +LUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, int nasize); +LUAI_FUNC void luaH_free (lua_State *L, Table *t); +LUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key); +LUAI_FUNC int luaH_getn (Table *t); + + +#if defined(LUA_DEBUG) +LUAI_FUNC Node *luaH_mainposition (const Table *t, const TValue *key); +LUAI_FUNC int luaH_isdummy (Node *n); +#endif + + +#endif diff --git a/lib/lua/ltablib.c b/lib/lua/ltablib.c new file mode 100644 index 000000000000..b6d9cb4ac74d --- /dev/null +++ b/lib/lua/ltablib.c @@ -0,0 +1,287 @@ +/* +** $Id: ltablib.c,v 1.38.1.3 2008/02/14 16:46:58 roberto Exp $ +** Library for Table Manipulation +** See Copyright Notice in lua.h +*/ + + +#include + +#define ltablib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +#define aux_getn(L,n) (luaL_checktype(L, n, LUA_TTABLE), luaL_getn(L, n)) + + +static int foreachi (lua_State *L) { + int i; + int n = aux_getn(L, 1); + luaL_checktype(L, 2, LUA_TFUNCTION); + for (i=1; i <= n; i++) { + lua_pushvalue(L, 2); /* function */ + lua_pushinteger(L, i); /* 1st argument */ + lua_rawgeti(L, 1, i); /* 2nd argument */ + lua_call(L, 2, 1); + if (!lua_isnil(L, -1)) + return 1; + lua_pop(L, 1); /* remove nil result */ + } + return 0; +} + + +static int foreach (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checktype(L, 2, LUA_TFUNCTION); + lua_pushnil(L); /* first key */ + while (lua_next(L, 1)) { + lua_pushvalue(L, 2); /* function */ + lua_pushvalue(L, -3); /* key */ + lua_pushvalue(L, -3); /* value */ + lua_call(L, 2, 1); + if (!lua_isnil(L, -1)) + return 1; + lua_pop(L, 2); /* remove value and result */ + } + return 0; +} + + +static int maxn (lua_State *L) { + lua_Number max = 0; + luaL_checktype(L, 1, LUA_TTABLE); + lua_pushnil(L); /* first key */ + while (lua_next(L, 1)) { + lua_pop(L, 1); /* remove value */ + if (lua_type(L, -1) == LUA_TNUMBER) { + lua_Number v = lua_tonumber(L, -1); + if (v > max) max = v; + } + } + lua_pushnumber(L, max); + return 1; +} + + +static int getn (lua_State *L) { + lua_pushinteger(L, aux_getn(L, 1)); + return 1; +} + + +static int setn (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); +#ifndef luaL_setn + luaL_setn(L, 1, luaL_checkint(L, 2)); +#else + luaL_error(L, LUA_QL("setn") " is obsolete"); +#endif + lua_pushvalue(L, 1); + return 1; +} + + +static int tinsert (lua_State *L) { + int e = aux_getn(L, 1) + 1; /* first empty element */ + int pos; /* where to insert new element */ + switch (lua_gettop(L)) { + case 2: { /* called with only 2 arguments */ + pos = e; /* insert new element at the end */ + break; + } + case 3: { + int i; + pos = luaL_checkint(L, 2); /* 2nd argument is the position */ + if (pos > e) e = pos; /* `grow' array if necessary */ + for (i = e; i > pos; i--) { /* move up elements */ + lua_rawgeti(L, 1, i-1); + lua_rawseti(L, 1, i); /* t[i] = t[i-1] */ + } + break; + } + default: { + return luaL_error(L, "wrong number of arguments to " LUA_QL("insert")); + } + } + luaL_setn(L, 1, e); /* new size */ + lua_rawseti(L, 1, pos); /* t[pos] = v */ + return 0; +} + + +static int tremove (lua_State *L) { + int e = aux_getn(L, 1); + int pos = luaL_optint(L, 2, e); + if (!(1 <= pos && pos <= e)) /* position is outside bounds? */ + return 0; /* nothing to remove */ + luaL_setn(L, 1, e - 1); /* t.n = n-1 */ + lua_rawgeti(L, 1, pos); /* result = t[pos] */ + for ( ;pos= P */ + while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) { + if (i>u) luaL_error(L, "invalid order function for sorting"); + lua_pop(L, 1); /* remove a[i] */ + } + /* repeat --j until a[j] <= P */ + while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) { + if (j + +#define ltm_c +#define LUA_CORE + +#include "lua.h" + +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" + + + +const char *const luaT_typenames[] = { + "nil", "boolean", "userdata", "number", + "string", "table", "function", "userdata", "thread", + "proto", "upval" +}; + + +void luaT_init (lua_State *L) { + static const char *const luaT_eventname[] = { /* ORDER TM */ + "__index", "__newindex", + "__gc", "__mode", "__eq", + "__add", "__sub", "__mul", "__div", "__mod", + "__pow", "__unm", "__len", "__lt", "__le", + "__concat", "__call" + }; + int i; + for (i=0; itmname[i] = luaS_new(L, luaT_eventname[i]); + luaS_fix(G(L)->tmname[i]); /* never collect these names */ + } +} + + +/* +** function to be used with macro "fasttm": optimized for absence of +** tag methods +*/ +const TValue *luaT_gettm (Table *events, TMS event, TString *ename) { + const TValue *tm = luaH_getstr(events, ename); + lua_assert(event <= TM_EQ); + if (ttisnil(tm)) { /* no tag method? */ + events->flags |= cast_byte(1u<metatable; + break; + case LUA_TUSERDATA: + mt = uvalue(o)->metatable; + break; + default: + mt = G(L)->mt[ttype(o)]; + } + return (mt ? luaH_getstr(mt, G(L)->tmname[event]) : luaO_nilobject); +} + diff --git a/lib/lua/ltm.h b/lib/lua/ltm.h new file mode 100644 index 000000000000..64343b781b64 --- /dev/null +++ b/lib/lua/ltm.h @@ -0,0 +1,54 @@ +/* +** $Id: ltm.h,v 2.6.1.1 2007/12/27 13:02:25 roberto Exp $ +** Tag methods +** See Copyright Notice in lua.h +*/ + +#ifndef ltm_h +#define ltm_h + + +#include "lobject.h" + + +/* +* WARNING: if you change the order of this enumeration, +* grep "ORDER TM" +*/ +typedef enum { + TM_INDEX, + TM_NEWINDEX, + TM_GC, + TM_MODE, + TM_EQ, /* last tag method with `fast' access */ + TM_ADD, + TM_SUB, + TM_MUL, + TM_DIV, + TM_MOD, + TM_POW, + TM_UNM, + TM_LEN, + TM_LT, + TM_LE, + TM_CONCAT, + TM_CALL, + TM_N /* number of elements in the enum */ +} TMS; + + + +#define gfasttm(g,et,e) ((et) == NULL ? NULL : \ + ((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e])) + +#define fasttm(l,et,e) gfasttm(G(l), et, e) + +LUAI_DATA const char *const luaT_typenames[]; + + +LUAI_FUNC const TValue *luaT_gettm (Table *events, TMS event, TString *ename); +LUAI_FUNC const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, + TMS event); +LUAI_FUNC void luaT_init (lua_State *L); + +#endif diff --git a/lib/lua/lua.c b/lib/lua/lua.c new file mode 100644 index 000000000000..3a46609328cb --- /dev/null +++ b/lib/lua/lua.c @@ -0,0 +1,392 @@ +/* +** $Id: lua.c,v 1.160.1.2 2007/12/28 15:32:23 roberto Exp $ +** Lua stand-alone interpreter +** See Copyright Notice in lua.h +*/ + + +#include +#include +#include +#include + +#define lua_c + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + + +static lua_State *globalL = NULL; + +static const char *progname = LUA_PROGNAME; + + + +static void lstop (lua_State *L, lua_Debug *ar) { + (void)ar; /* unused arg. */ + lua_sethook(L, NULL, 0, 0); + luaL_error(L, "interrupted!"); +} + + +static void laction (int i) { + signal(i, SIG_DFL); /* if another SIGINT happens before lstop, + terminate process (default action) */ + lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1); +} + + +static void print_usage (void) { + fprintf(stderr, + "usage: %s [options] [script [args]].\n" + "Available options are:\n" + " -e stat execute string " LUA_QL("stat") "\n" + " -l name require library " LUA_QL("name") "\n" + " -i enter interactive mode after executing " LUA_QL("script") "\n" + " -v show version information\n" + " -- stop handling options\n" + " - execute stdin and stop handling options\n" + , + progname); + fflush(stderr); +} + + +static void l_message (const char *pname, const char *msg) { + if (pname) fprintf(stderr, "%s: ", pname); + fprintf(stderr, "%s\n", msg); + fflush(stderr); +} + + +static int report (lua_State *L, int status) { + if (status && !lua_isnil(L, -1)) { + const char *msg = lua_tostring(L, -1); + if (msg == NULL) msg = "(error object is not a string)"; + l_message(progname, msg); + lua_pop(L, 1); + } + return status; +} + + +static int traceback (lua_State *L) { + if (!lua_isstring(L, 1)) /* 'message' not a string? */ + return 1; /* keep it intact */ + lua_getfield(L, LUA_GLOBALSINDEX, "debug"); + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + return 1; + } + lua_getfield(L, -1, "traceback"); + if (!lua_isfunction(L, -1)) { + lua_pop(L, 2); + return 1; + } + lua_pushvalue(L, 1); /* pass error message */ + lua_pushinteger(L, 2); /* skip this function and traceback */ + lua_call(L, 2, 1); /* call debug.traceback */ + return 1; +} + + +static int docall (lua_State *L, int narg, int clear) { + int status; + int base = lua_gettop(L) - narg; /* function index */ + lua_pushcfunction(L, traceback); /* push traceback function */ + lua_insert(L, base); /* put it under chunk and args */ + signal(SIGINT, laction); + status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base); + signal(SIGINT, SIG_DFL); + lua_remove(L, base); /* remove traceback function */ + /* force a complete garbage collection in case of errors */ + if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0); + return status; +} + + +static void print_version (void) { + l_message(NULL, LUA_RELEASE " " LUA_COPYRIGHT); +} + + +static int getargs (lua_State *L, char **argv, int n) { + int narg; + int i; + int argc = 0; + while (argv[argc]) argc++; /* count total number of arguments */ + narg = argc - (n + 1); /* number of arguments to the script */ + luaL_checkstack(L, narg + 3, "too many arguments to script"); + for (i=n+1; i < argc; i++) + lua_pushstring(L, argv[i]); + lua_createtable(L, narg, n + 1); + for (i=0; i < argc; i++) { + lua_pushstring(L, argv[i]); + lua_rawseti(L, -2, i - n); + } + return narg; +} + + +static int dofile (lua_State *L, const char *name) { + int status = luaL_loadfile(L, name) || docall(L, 0, 1); + return report(L, status); +} + + +static int dostring (lua_State *L, const char *s, const char *name) { + int status = luaL_loadbuffer(L, s, strlen(s), name) || docall(L, 0, 1); + return report(L, status); +} + + +static int dolibrary (lua_State *L, const char *name) { + lua_getglobal(L, "require"); + lua_pushstring(L, name); + return report(L, docall(L, 1, 1)); +} + + +static const char *get_prompt (lua_State *L, int firstline) { + const char *p; + lua_getfield(L, LUA_GLOBALSINDEX, firstline ? "_PROMPT" : "_PROMPT2"); + p = lua_tostring(L, -1); + if (p == NULL) p = (firstline ? LUA_PROMPT : LUA_PROMPT2); + lua_pop(L, 1); /* remove global */ + return p; +} + + +static int incomplete (lua_State *L, int status) { + if (status == LUA_ERRSYNTAX) { + size_t lmsg; + const char *msg = lua_tolstring(L, -1, &lmsg); + const char *tp = msg + lmsg - (sizeof(LUA_QL("")) - 1); + if (strstr(msg, LUA_QL("")) == tp) { + lua_pop(L, 1); + return 1; + } + } + return 0; /* else... */ +} + + +static int pushline (lua_State *L, int firstline) { + char buffer[LUA_MAXINPUT]; + char *b = buffer; + size_t l; + const char *prmt = get_prompt(L, firstline); + if (lua_readline(L, b, prmt) == 0) + return 0; /* no input */ + l = strlen(b); + if (l > 0 && b[l-1] == '\n') /* line ends with newline? */ + b[l-1] = '\0'; /* remove it */ + if (firstline && b[0] == '=') /* first line starts with `=' ? */ + lua_pushfstring(L, "return %s", b+1); /* change it to `return' */ + else + lua_pushstring(L, b); + lua_freeline(L, b); + return 1; +} + + +static int loadline (lua_State *L) { + int status; + lua_settop(L, 0); + if (!pushline(L, 1)) + return -1; /* no input */ + for (;;) { /* repeat until gets a complete line */ + status = luaL_loadbuffer(L, lua_tostring(L, 1), lua_strlen(L, 1), "=stdin"); + if (!incomplete(L, status)) break; /* cannot try to add lines? */ + if (!pushline(L, 0)) /* no more input? */ + return -1; + lua_pushliteral(L, "\n"); /* add a new line... */ + lua_insert(L, -2); /* ...between the two lines */ + lua_concat(L, 3); /* join them */ + } + lua_saveline(L, 1); + lua_remove(L, 1); /* remove line */ + return status; +} + + +static void dotty (lua_State *L) { + int status; + const char *oldprogname = progname; + progname = NULL; + while ((status = loadline(L)) != -1) { + if (status == 0) status = docall(L, 0, 0); + report(L, status); + if (status == 0 && lua_gettop(L) > 0) { /* any result to print? */ + lua_getglobal(L, "print"); + lua_insert(L, 1); + if (lua_pcall(L, lua_gettop(L)-1, 0, 0) != 0) + l_message(progname, lua_pushfstring(L, + "error calling " LUA_QL("print") " (%s)", + lua_tostring(L, -1))); + } + } + lua_settop(L, 0); /* clear stack */ + fputs("\n", stdout); + fflush(stdout); + progname = oldprogname; +} + + +static int handle_script (lua_State *L, char **argv, int n) { + int status; + const char *fname; + int narg = getargs(L, argv, n); /* collect arguments */ + lua_setglobal(L, "arg"); + fname = argv[n]; + if (strcmp(fname, "-") == 0 && strcmp(argv[n-1], "--") != 0) + fname = NULL; /* stdin */ + status = luaL_loadfile(L, fname); + lua_insert(L, -(narg+1)); + if (status == 0) + status = docall(L, narg, 0); + else + lua_pop(L, narg); + return report(L, status); +} + + +/* check that argument has no extra characters at the end */ +#define notail(x) {if ((x)[2] != '\0') return -1;} + + +static int collectargs (char **argv, int *pi, int *pv, int *pe) { + int i; + for (i = 1; argv[i] != NULL; i++) { + if (argv[i][0] != '-') /* not an option? */ + return i; + switch (argv[i][1]) { /* option */ + case '-': + notail(argv[i]); + return (argv[i+1] != NULL ? i+1 : 0); + case '\0': + return i; + case 'i': + notail(argv[i]); + *pi = 1; /* go through */ + case 'v': + notail(argv[i]); + *pv = 1; + break; + case 'e': + *pe = 1; /* go through */ + case 'l': + if (argv[i][2] == '\0') { + i++; + if (argv[i] == NULL) return -1; + } + break; + default: return -1; /* invalid option */ + } + } + return 0; +} + + +static int runargs (lua_State *L, char **argv, int n) { + int i; + for (i = 1; i < n; i++) { + if (argv[i] == NULL) continue; + lua_assert(argv[i][0] == '-'); + switch (argv[i][1]) { /* option */ + case 'e': { + const char *chunk = argv[i] + 2; + if (*chunk == '\0') chunk = argv[++i]; + lua_assert(chunk != NULL); + if (dostring(L, chunk, "=(command line)") != 0) + return 1; + break; + } + case 'l': { + const char *filename = argv[i] + 2; + if (*filename == '\0') filename = argv[++i]; + lua_assert(filename != NULL); + if (dolibrary(L, filename)) + return 1; /* stop if file fails */ + break; + } + default: break; + } + } + return 0; +} + + +static int handle_luainit (lua_State *L) { + const char *init = getenv(LUA_INIT); + if (init == NULL) return 0; /* status OK */ + else if (init[0] == '@') + return dofile(L, init+1); + else + return dostring(L, init, "=" LUA_INIT); +} + + +struct Smain { + int argc; + char **argv; + int status; +}; + + +static int pmain (lua_State *L) { + struct Smain *s = (struct Smain *)lua_touserdata(L, 1); + char **argv = s->argv; + int script; + int has_i = 0, has_v = 0, has_e = 0; + globalL = L; + if (argv[0] && argv[0][0]) progname = argv[0]; + lua_gc(L, LUA_GCSTOP, 0); /* stop collector during initialization */ + luaL_openlibs(L); /* open libraries */ + lua_gc(L, LUA_GCRESTART, 0); + s->status = handle_luainit(L); + if (s->status != 0) return 0; + script = collectargs(argv, &has_i, &has_v, &has_e); + if (script < 0) { /* invalid args? */ + print_usage(); + s->status = 1; + return 0; + } + if (has_v) print_version(); + s->status = runargs(L, argv, (script > 0) ? script : s->argc); + if (s->status != 0) return 0; + if (script) + s->status = handle_script(L, argv, script); + if (s->status != 0) return 0; + if (has_i) + dotty(L); + else if (script == 0 && !has_e && !has_v) { + if (lua_stdin_is_tty()) { + print_version(); + dotty(L); + } + else dofile(L, NULL); /* executes stdin as a file */ + } + return 0; +} + + +int main (int argc, char **argv) { + int status; + struct Smain s; + lua_State *L = lua_open(); /* create state */ + if (L == NULL) { + l_message(argv[0], "cannot create state: not enough memory"); + return EXIT_FAILURE; + } + s.argc = argc; + s.argv = argv; + status = lua_cpcall(L, &pmain, &s); + report(L, status); + lua_close(L); + return (status || s.status) ? EXIT_FAILURE : EXIT_SUCCESS; +} + diff --git a/lib/lua/lua.h b/lib/lua/lua.h new file mode 100644 index 000000000000..a4b73e743ede --- /dev/null +++ b/lib/lua/lua.h @@ -0,0 +1,388 @@ +/* +** $Id: lua.h,v 1.218.1.7 2012/01/13 20:36:20 roberto Exp $ +** Lua - An Extensible Extension Language +** Lua.org, PUC-Rio, Brazil (http://www.lua.org) +** See Copyright Notice at the end of this file +*/ + + +#ifndef lua_h +#define lua_h + +#include +#include + + +#include "luaconf.h" + + +#define LUA_VERSION "Lua 5.1" +#define LUA_RELEASE "Lua 5.1.5" +#define LUA_VERSION_NUM 501 +#define LUA_COPYRIGHT "Copyright (C) 1994-2012 Lua.org, PUC-Rio" +#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo & W. Celes" + + +/* mark for precompiled code (`Lua') */ +#define LUA_SIGNATURE "\033Lua" + +/* option for multiple returns in `lua_pcall' and `lua_call' */ +#define LUA_MULTRET (-1) + + +/* +** pseudo-indices +*/ +#define LUA_REGISTRYINDEX (-10000) +#define LUA_ENVIRONINDEX (-10001) +#define LUA_GLOBALSINDEX (-10002) +#define lua_upvalueindex(i) (LUA_GLOBALSINDEX-(i)) + + +/* thread status; 0 is OK */ +#define LUA_YIELD 1 +#define LUA_ERRRUN 2 +#define LUA_ERRSYNTAX 3 +#define LUA_ERRMEM 4 +#define LUA_ERRERR 5 + + +typedef struct lua_State lua_State; + +typedef int (*lua_CFunction) (lua_State *L); + + +/* +** functions that read/write blocks when loading/dumping Lua chunks +*/ +typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz); + +typedef int (*lua_Writer) (lua_State *L, const void* p, size_t sz, void* ud); + + +/* +** prototype for memory-allocation functions +*/ +typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); + + +/* +** basic types +*/ +#define LUA_TNONE (-1) + +#define LUA_TNIL 0 +#define LUA_TBOOLEAN 1 +#define LUA_TLIGHTUSERDATA 2 +#define LUA_TNUMBER 3 +#define LUA_TSTRING 4 +#define LUA_TTABLE 5 +#define LUA_TFUNCTION 6 +#define LUA_TUSERDATA 7 +#define LUA_TTHREAD 8 + + + +/* minimum Lua stack available to a C function */ +#define LUA_MINSTACK 20 + + +/* +** generic extra include file +*/ +#if defined(LUA_USER_H) +#include LUA_USER_H +#endif + + +/* type of numbers in Lua */ +typedef LUA_NUMBER lua_Number; + + +/* type for integer functions */ +typedef LUA_INTEGER lua_Integer; + + + +/* +** state manipulation +*/ +LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud); +LUA_API void (lua_close) (lua_State *L); +LUA_API lua_State *(lua_newthread) (lua_State *L); + +LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf); + + +/* +** basic stack manipulation +*/ +LUA_API int (lua_gettop) (lua_State *L); +LUA_API void (lua_settop) (lua_State *L, int idx); +LUA_API void (lua_pushvalue) (lua_State *L, int idx); +LUA_API void (lua_remove) (lua_State *L, int idx); +LUA_API void (lua_insert) (lua_State *L, int idx); +LUA_API void (lua_replace) (lua_State *L, int idx); +LUA_API int (lua_checkstack) (lua_State *L, int sz); + +LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n); + + +/* +** access functions (stack -> C) +*/ + +LUA_API int (lua_isnumber) (lua_State *L, int idx); +LUA_API int (lua_isstring) (lua_State *L, int idx); +LUA_API int (lua_iscfunction) (lua_State *L, int idx); +LUA_API int (lua_isuserdata) (lua_State *L, int idx); +LUA_API int (lua_type) (lua_State *L, int idx); +LUA_API const char *(lua_typename) (lua_State *L, int tp); + +LUA_API int (lua_equal) (lua_State *L, int idx1, int idx2); +LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2); +LUA_API int (lua_lessthan) (lua_State *L, int idx1, int idx2); + +LUA_API lua_Number (lua_tonumber) (lua_State *L, int idx); +LUA_API lua_Integer (lua_tointeger) (lua_State *L, int idx); +LUA_API int (lua_toboolean) (lua_State *L, int idx); +LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len); +LUA_API size_t (lua_objlen) (lua_State *L, int idx); +LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx); +LUA_API void *(lua_touserdata) (lua_State *L, int idx); +LUA_API lua_State *(lua_tothread) (lua_State *L, int idx); +LUA_API const void *(lua_topointer) (lua_State *L, int idx); + + +/* +** push functions (C -> stack) +*/ +LUA_API void (lua_pushnil) (lua_State *L); +LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n); +LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n); +LUA_API void (lua_pushlstring) (lua_State *L, const char *s, size_t l); +LUA_API void (lua_pushstring) (lua_State *L, const char *s); +LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt, + va_list argp); +LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...); +LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n); +LUA_API void (lua_pushboolean) (lua_State *L, int b); +LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p); +LUA_API int (lua_pushthread) (lua_State *L); + + +/* +** get functions (Lua -> stack) +*/ +LUA_API void (lua_gettable) (lua_State *L, int idx); +LUA_API void (lua_getfield) (lua_State *L, int idx, const char *k); +LUA_API void (lua_rawget) (lua_State *L, int idx); +LUA_API void (lua_rawgeti) (lua_State *L, int idx, int n); +LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec); +LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz); +LUA_API int (lua_getmetatable) (lua_State *L, int objindex); +LUA_API void (lua_getfenv) (lua_State *L, int idx); + + +/* +** set functions (stack -> Lua) +*/ +LUA_API void (lua_settable) (lua_State *L, int idx); +LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k); +LUA_API void (lua_rawset) (lua_State *L, int idx); +LUA_API void (lua_rawseti) (lua_State *L, int idx, int n); +LUA_API int (lua_setmetatable) (lua_State *L, int objindex); +LUA_API int (lua_setfenv) (lua_State *L, int idx); + + +/* +** `load' and `call' functions (load and run Lua code) +*/ +LUA_API void (lua_call) (lua_State *L, int nargs, int nresults); +LUA_API int (lua_pcall) (lua_State *L, int nargs, int nresults, int errfunc); +LUA_API int (lua_cpcall) (lua_State *L, lua_CFunction func, void *ud); +LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt, + const char *chunkname); + +LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data); + + +/* +** coroutine functions +*/ +LUA_API int (lua_yield) (lua_State *L, int nresults); +LUA_API int (lua_resume) (lua_State *L, int narg); +LUA_API int (lua_status) (lua_State *L); + +/* +** garbage-collection function and options +*/ + +#define LUA_GCSTOP 0 +#define LUA_GCRESTART 1 +#define LUA_GCCOLLECT 2 +#define LUA_GCCOUNT 3 +#define LUA_GCCOUNTB 4 +#define LUA_GCSTEP 5 +#define LUA_GCSETPAUSE 6 +#define LUA_GCSETSTEPMUL 7 + +LUA_API int (lua_gc) (lua_State *L, int what, int data); + + +/* +** miscellaneous functions +*/ + +LUA_API int (lua_error) (lua_State *L); + +LUA_API int (lua_next) (lua_State *L, int idx); + +LUA_API void (lua_concat) (lua_State *L, int n); + +LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud); +LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud); + + + +/* +** =============================================================== +** some useful macros +** =============================================================== +*/ + +#define lua_pop(L,n) lua_settop(L, -(n)-1) + +#define lua_newtable(L) lua_createtable(L, 0, 0) + +#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n))) + +#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0) + +#define lua_strlen(L,i) lua_objlen(L, (i)) + +#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION) +#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE) +#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA) +#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL) +#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN) +#define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD) +#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE) +#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0) + +#define lua_pushliteral(L, s) \ + lua_pushlstring(L, "" s, (sizeof(s)/sizeof(char))-1) + +#define lua_setglobal(L,s) lua_setfield(L, LUA_GLOBALSINDEX, (s)) +#define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, (s)) + +#define lua_tostring(L,i) lua_tolstring(L, (i), NULL) + + + +/* +** compatibility macros and functions +*/ + +#define lua_open() luaL_newstate() + +#define lua_getregistry(L) lua_pushvalue(L, LUA_REGISTRYINDEX) + +#define lua_getgccount(L) lua_gc(L, LUA_GCCOUNT, 0) + +#define lua_Chunkreader lua_Reader +#define lua_Chunkwriter lua_Writer + + +/* hack */ +LUA_API void lua_setlevel (lua_State *from, lua_State *to); + + +/* +** {====================================================================== +** Debug API +** ======================================================================= +*/ + + +/* +** Event codes +*/ +#define LUA_HOOKCALL 0 +#define LUA_HOOKRET 1 +#define LUA_HOOKLINE 2 +#define LUA_HOOKCOUNT 3 +#define LUA_HOOKTAILRET 4 + + +/* +** Event masks +*/ +#define LUA_MASKCALL (1 << LUA_HOOKCALL) +#define LUA_MASKRET (1 << LUA_HOOKRET) +#define LUA_MASKLINE (1 << LUA_HOOKLINE) +#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT) + +typedef struct lua_Debug lua_Debug; /* activation record */ + + +/* Functions to be called by the debuger in specific events */ +typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar); + + +LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar); +LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar); +LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n); +LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n); +LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n); +LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n); + +LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count); +LUA_API lua_Hook lua_gethook (lua_State *L); +LUA_API int lua_gethookmask (lua_State *L); +LUA_API int lua_gethookcount (lua_State *L); + + +struct lua_Debug { + int event; + const char *name; /* (n) */ + const char *namewhat; /* (n) `global', `local', `field', `method' */ + const char *what; /* (S) `Lua', `C', `main', `tail' */ + const char *source; /* (S) */ + int currentline; /* (l) */ + int nups; /* (u) number of upvalues */ + int linedefined; /* (S) */ + int lastlinedefined; /* (S) */ + char short_src[LUA_IDSIZE]; /* (S) */ + /* private part */ + int i_ci; /* active function */ +}; + +/* }====================================================================== */ + + +/****************************************************************************** +* Copyright (C) 1994-2012 Lua.org, PUC-Rio. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +******************************************************************************/ + + +#endif diff --git a/lib/lua/luac.c b/lib/lua/luac.c new file mode 100644 index 000000000000..d07017391bfa --- /dev/null +++ b/lib/lua/luac.c @@ -0,0 +1,200 @@ +/* +** $Id: luac.c,v 1.54 2006/06/02 17:37:11 lhf Exp $ +** Lua compiler (saves bytecodes to files; also list bytecodes) +** See Copyright Notice in lua.h +*/ + +#include +#include +#include +#include + +#define luac_c +#define LUA_CORE + +#include "lua.h" +#include "lauxlib.h" + +#include "ldo.h" +#include "lfunc.h" +#include "lmem.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lstring.h" +#include "lundump.h" + +#define PROGNAME "luac" /* default program name */ +#define OUTPUT PROGNAME ".out" /* default output file */ + +static int listing=0; /* list bytecodes? */ +static int dumping=1; /* dump bytecodes? */ +static int stripping=0; /* strip debug information? */ +static char Output[]={ OUTPUT }; /* default output file name */ +static const char* output=Output; /* actual output file name */ +static const char* progname=PROGNAME; /* actual program name */ + +static void fatal(const char* message) +{ + fprintf(stderr,"%s: %s\n",progname,message); + exit(EXIT_FAILURE); +} + +static void cannot(const char* what) +{ + fprintf(stderr,"%s: cannot %s %s: %s\n",progname,what,output,strerror(errno)); + exit(EXIT_FAILURE); +} + +static void usage(const char* message) +{ + if (*message=='-') + fprintf(stderr,"%s: unrecognized option " LUA_QS "\n",progname,message); + else + fprintf(stderr,"%s: %s\n",progname,message); + fprintf(stderr, + "usage: %s [options] [filenames].\n" + "Available options are:\n" + " - process stdin\n" + " -l list\n" + " -o name output to file " LUA_QL("name") " (default is \"%s\")\n" + " -p parse only\n" + " -s strip debug information\n" + " -v show version information\n" + " -- stop handling options\n", + progname,Output); + exit(EXIT_FAILURE); +} + +#define IS(s) (strcmp(argv[i],s)==0) + +static int doargs(int argc, char* argv[]) +{ + int i; + int version=0; + if (argv[0]!=NULL && *argv[0]!=0) progname=argv[0]; + for (i=1; itop+(i))->l.p) + +static const Proto* combine(lua_State* L, int n) +{ + if (n==1) + return toproto(L,-1); + else + { + int i,pc; + Proto* f=luaF_newproto(L); + setptvalue2s(L,L->top,f); incr_top(L); + f->source=luaS_newliteral(L,"=(" PROGNAME ")"); + f->maxstacksize=1; + pc=2*n+1; + f->code=luaM_newvector(L,pc,Instruction); + f->sizecode=pc; + f->p=luaM_newvector(L,n,Proto*); + f->sizep=n; + pc=0; + for (i=0; ip[i]=toproto(L,i-n-1); + f->code[pc++]=CREATE_ABx(OP_CLOSURE,0,i); + f->code[pc++]=CREATE_ABC(OP_CALL,0,1,1); + } + f->code[pc++]=CREATE_ABC(OP_RETURN,0,1,0); + return f; + } +} + +static int writer(lua_State* L, const void* p, size_t size, void* u) +{ + UNUSED(L); + return (fwrite(p,size,1,(FILE*)u)!=1) && (size!=0); +} + +struct Smain { + int argc; + char** argv; +}; + +static int pmain(lua_State* L) +{ + struct Smain* s = (struct Smain*)lua_touserdata(L, 1); + int argc=s->argc; + char** argv=s->argv; + const Proto* f; + int i; + if (!lua_checkstack(L,argc)) fatal("too many input files"); + for (i=0; i1); + if (dumping) + { + FILE* D= (output==NULL) ? stdout : fopen(output,"wb"); + if (D==NULL) cannot("open"); + lua_lock(L); + luaU_dump(L,f,writer,D,stripping); + lua_unlock(L); + if (ferror(D)) cannot("write"); + if (fclose(D)) cannot("close"); + } + return 0; +} + +int main(int argc, char* argv[]) +{ + lua_State* L; + struct Smain s; + int i=doargs(argc,argv); + argc-=i; argv+=i; + if (argc<=0) usage("no input files given"); + L=lua_open(); + if (L==NULL) fatal("not enough memory for state"); + s.argc=argc; + s.argv=argv; + if (lua_cpcall(L,pmain,&s)!=0) fatal(lua_tostring(L,-1)); + lua_close(L); + return EXIT_SUCCESS; +} diff --git a/lib/lua/luaconf.h b/lib/lua/luaconf.h new file mode 100644 index 000000000000..e2cb26163a23 --- /dev/null +++ b/lib/lua/luaconf.h @@ -0,0 +1,763 @@ +/* +** $Id: luaconf.h,v 1.82.1.7 2008/02/11 16:25:08 roberto Exp $ +** Configuration file for Lua +** See Copyright Notice in lua.h +*/ + + +#ifndef lconfig_h +#define lconfig_h + +#include +#include + + +/* +** ================================================================== +** Search for "@@" to find all configurable definitions. +** =================================================================== +*/ + + +/* +@@ LUA_ANSI controls the use of non-ansi features. +** CHANGE it (define it) if you want Lua to avoid the use of any +** non-ansi feature or library. +*/ +#if defined(__STRICT_ANSI__) +#define LUA_ANSI +#endif + + +#if !defined(LUA_ANSI) && defined(_WIN32) +#define LUA_WIN +#endif + +#if defined(LUA_USE_LINUX) +#define LUA_USE_POSIX +#define LUA_USE_DLOPEN /* needs an extra library: -ldl */ +#define LUA_USE_READLINE /* needs some extra libraries */ +#endif + +#if defined(LUA_USE_MACOSX) +#define LUA_USE_POSIX +#define LUA_DL_DYLD /* does not need extra library */ +#endif + + + +/* +@@ LUA_USE_POSIX includes all functionallity listed as X/Open System +@* Interfaces Extension (XSI). +** CHANGE it (define it) if your system is XSI compatible. +*/ +#if defined(LUA_USE_POSIX) +#define LUA_USE_MKSTEMP +#define LUA_USE_ISATTY +#define LUA_USE_POPEN +#define LUA_USE_ULONGJMP +#endif + + +/* +@@ LUA_PATH and LUA_CPATH are the names of the environment variables that +@* Lua check to set its paths. +@@ LUA_INIT is the name of the environment variable that Lua +@* checks for initialization code. +** CHANGE them if you want different names. +*/ +#define LUA_PATH "LUA_PATH" +#define LUA_CPATH "LUA_CPATH" +#define LUA_INIT "LUA_INIT" + + +/* +@@ LUA_PATH_DEFAULT is the default path that Lua uses to look for +@* Lua libraries. +@@ LUA_CPATH_DEFAULT is the default path that Lua uses to look for +@* C libraries. +** CHANGE them if your machine has a non-conventional directory +** hierarchy or if you want to install your libraries in +** non-conventional directories. +*/ +#if defined(_WIN32) +/* +** In Windows, any exclamation mark ('!') in the path is replaced by the +** path of the directory of the executable file of the current process. +*/ +#define LUA_LDIR "!\\lua\\" +#define LUA_CDIR "!\\" +#define LUA_PATH_DEFAULT \ + ".\\?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \ + LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua" +#define LUA_CPATH_DEFAULT \ + ".\\?.dll;" LUA_CDIR"?.dll;" LUA_CDIR"loadall.dll" + +#else +#define LUA_ROOT "/usr/local/" +#define LUA_LDIR LUA_ROOT "share/lua/5.1/" +#define LUA_CDIR LUA_ROOT "lib/lua/5.1/" +#define LUA_PATH_DEFAULT \ + "./?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \ + LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua" +#define LUA_CPATH_DEFAULT \ + "./?.so;" LUA_CDIR"?.so;" LUA_CDIR"loadall.so" +#endif + + +/* +@@ LUA_DIRSEP is the directory separator (for submodules). +** CHANGE it if your machine does not use "/" as the directory separator +** and is not Windows. (On Windows Lua automatically uses "\".) +*/ +#if defined(_WIN32) +#define LUA_DIRSEP "\\" +#else +#define LUA_DIRSEP "/" +#endif + + +/* +@@ LUA_PATHSEP is the character that separates templates in a path. +@@ LUA_PATH_MARK is the string that marks the substitution points in a +@* template. +@@ LUA_EXECDIR in a Windows path is replaced by the executable's +@* directory. +@@ LUA_IGMARK is a mark to ignore all before it when bulding the +@* luaopen_ function name. +** CHANGE them if for some reason your system cannot use those +** characters. (E.g., if one of those characters is a common character +** in file/directory names.) Probably you do not need to change them. +*/ +#define LUA_PATHSEP ";" +#define LUA_PATH_MARK "?" +#define LUA_EXECDIR "!" +#define LUA_IGMARK "-" + + +/* +@@ LUA_INTEGER is the integral type used by lua_pushinteger/lua_tointeger. +** CHANGE that if ptrdiff_t is not adequate on your machine. (On most +** machines, ptrdiff_t gives a good choice between int or long.) +*/ +#define LUA_INTEGER ptrdiff_t + + +/* +@@ LUA_API is a mark for all core API functions. +@@ LUALIB_API is a mark for all standard library functions. +** CHANGE them if you need to define those functions in some special way. +** For instance, if you want to create one Windows DLL with the core and +** the libraries, you may want to use the following definition (define +** LUA_BUILD_AS_DLL to get it). +*/ +#if defined(LUA_BUILD_AS_DLL) + +#if defined(LUA_CORE) || defined(LUA_LIB) +#define LUA_API __declspec(dllexport) +#else +#define LUA_API __declspec(dllimport) +#endif + +#else + +#define LUA_API extern + +#endif + +/* more often than not the libs go together with the core */ +#define LUALIB_API LUA_API + + +/* +@@ LUAI_FUNC is a mark for all extern functions that are not to be +@* exported to outside modules. +@@ LUAI_DATA is a mark for all extern (const) variables that are not to +@* be exported to outside modules. +** CHANGE them if you need to mark them in some special way. Elf/gcc +** (versions 3.2 and later) mark them as "hidden" to optimize access +** when Lua is compiled as a shared library. +*/ +#if defined(luaall_c) +#define LUAI_FUNC static +#define LUAI_DATA /* empty */ + +#elif defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \ + defined(__ELF__) +#define LUAI_FUNC __attribute__((visibility("hidden"))) extern +#define LUAI_DATA LUAI_FUNC + +#else +#define LUAI_FUNC extern +#define LUAI_DATA extern +#endif + + + +/* +@@ LUA_QL describes how error messages quote program elements. +** CHANGE it if you want a different appearance. +*/ +#define LUA_QL(x) "'" x "'" +#define LUA_QS LUA_QL("%s") + + +/* +@@ LUA_IDSIZE gives the maximum size for the description of the source +@* of a function in debug information. +** CHANGE it if you want a different size. +*/ +#define LUA_IDSIZE 60 + + +/* +** {================================================================== +** Stand-alone configuration +** =================================================================== +*/ + +#if defined(lua_c) || defined(luaall_c) + +/* +@@ lua_stdin_is_tty detects whether the standard input is a 'tty' (that +@* is, whether we're running lua interactively). +** CHANGE it if you have a better definition for non-POSIX/non-Windows +** systems. +*/ +#if defined(LUA_USE_ISATTY) +#include +#define lua_stdin_is_tty() isatty(0) +#elif defined(LUA_WIN) +#include +#include +#define lua_stdin_is_tty() _isatty(_fileno(stdin)) +#else +#define lua_stdin_is_tty() 1 /* assume stdin is a tty */ +#endif + + +/* +@@ LUA_PROMPT is the default prompt used by stand-alone Lua. +@@ LUA_PROMPT2 is the default continuation prompt used by stand-alone Lua. +** CHANGE them if you want different prompts. (You can also change the +** prompts dynamically, assigning to globals _PROMPT/_PROMPT2.) +*/ +#define LUA_PROMPT "> " +#define LUA_PROMPT2 ">> " + + +/* +@@ LUA_PROGNAME is the default name for the stand-alone Lua program. +** CHANGE it if your stand-alone interpreter has a different name and +** your system is not able to detect that name automatically. +*/ +#define LUA_PROGNAME "lua" + + +/* +@@ LUA_MAXINPUT is the maximum length for an input line in the +@* stand-alone interpreter. +** CHANGE it if you need longer lines. +*/ +#define LUA_MAXINPUT 512 + + +/* +@@ lua_readline defines how to show a prompt and then read a line from +@* the standard input. +@@ lua_saveline defines how to "save" a read line in a "history". +@@ lua_freeline defines how to free a line read by lua_readline. +** CHANGE them if you want to improve this functionality (e.g., by using +** GNU readline and history facilities). +*/ +#if defined(LUA_USE_READLINE) +#include +#include +#include +#define lua_readline(L,b,p) ((void)L, ((b)=readline(p)) != NULL) +#define lua_saveline(L,idx) \ + if (lua_strlen(L,idx) > 0) /* non-empty line? */ \ + add_history(lua_tostring(L, idx)); /* add it to history */ +#define lua_freeline(L,b) ((void)L, free(b)) +#else +#define lua_readline(L,b,p) \ + ((void)L, fputs(p, stdout), fflush(stdout), /* show prompt */ \ + fgets(b, LUA_MAXINPUT, stdin) != NULL) /* get line */ +#define lua_saveline(L,idx) { (void)L; (void)idx; } +#define lua_freeline(L,b) { (void)L; (void)b; } +#endif + +#endif + +/* }================================================================== */ + + +/* +@@ LUAI_GCPAUSE defines the default pause between garbage-collector cycles +@* as a percentage. +** CHANGE it if you want the GC to run faster or slower (higher values +** mean larger pauses which mean slower collection.) You can also change +** this value dynamically. +*/ +#define LUAI_GCPAUSE 200 /* 200% (wait memory to double before next GC) */ + + +/* +@@ LUAI_GCMUL defines the default speed of garbage collection relative to +@* memory allocation as a percentage. +** CHANGE it if you want to change the granularity of the garbage +** collection. (Higher values mean coarser collections. 0 represents +** infinity, where each step performs a full collection.) You can also +** change this value dynamically. +*/ +#define LUAI_GCMUL 200 /* GC runs 'twice the speed' of memory allocation */ + + + +/* +@@ LUA_COMPAT_GETN controls compatibility with old getn behavior. +** CHANGE it (define it) if you want exact compatibility with the +** behavior of setn/getn in Lua 5.0. +*/ +#undef LUA_COMPAT_GETN + +/* +@@ LUA_COMPAT_LOADLIB controls compatibility about global loadlib. +** CHANGE it to undefined as soon as you do not need a global 'loadlib' +** function (the function is still available as 'package.loadlib'). +*/ +#undef LUA_COMPAT_LOADLIB + +/* +@@ LUA_COMPAT_VARARG controls compatibility with old vararg feature. +** CHANGE it to undefined as soon as your programs use only '...' to +** access vararg parameters (instead of the old 'arg' table). +*/ +#define LUA_COMPAT_VARARG + +/* +@@ LUA_COMPAT_MOD controls compatibility with old math.mod function. +** CHANGE it to undefined as soon as your programs use 'math.fmod' or +** the new '%' operator instead of 'math.mod'. +*/ +#define LUA_COMPAT_MOD + +/* +@@ LUA_COMPAT_LSTR controls compatibility with old long string nesting +@* facility. +** CHANGE it to 2 if you want the old behaviour, or undefine it to turn +** off the advisory error when nesting [[...]]. +*/ +#define LUA_COMPAT_LSTR 1 + +/* +@@ LUA_COMPAT_GFIND controls compatibility with old 'string.gfind' name. +** CHANGE it to undefined as soon as you rename 'string.gfind' to +** 'string.gmatch'. +*/ +#define LUA_COMPAT_GFIND + +/* +@@ LUA_COMPAT_OPENLIB controls compatibility with old 'luaL_openlib' +@* behavior. +** CHANGE it to undefined as soon as you replace to 'luaL_register' +** your uses of 'luaL_openlib' +*/ +#define LUA_COMPAT_OPENLIB + + + +/* +@@ luai_apicheck is the assert macro used by the Lua-C API. +** CHANGE luai_apicheck if you want Lua to perform some checks in the +** parameters it gets from API calls. This may slow down the interpreter +** a bit, but may be quite useful when debugging C code that interfaces +** with Lua. A useful redefinition is to use assert.h. +*/ +#if defined(LUA_USE_APICHECK) +#include +#define luai_apicheck(L,o) { (void)L; assert(o); } +#else +#define luai_apicheck(L,o) { (void)L; } +#endif + + +/* +@@ LUAI_BITSINT defines the number of bits in an int. +** CHANGE here if Lua cannot automatically detect the number of bits of +** your machine. Probably you do not need to change this. +*/ +/* avoid overflows in comparison */ +#if INT_MAX-20 < 32760 +#define LUAI_BITSINT 16 +#elif INT_MAX > 2147483640L +/* int has at least 32 bits */ +#define LUAI_BITSINT 32 +#else +#error "you must define LUA_BITSINT with number of bits in an integer" +#endif + + +/* +@@ LUAI_UINT32 is an unsigned integer with at least 32 bits. +@@ LUAI_INT32 is an signed integer with at least 32 bits. +@@ LUAI_UMEM is an unsigned integer big enough to count the total +@* memory used by Lua. +@@ LUAI_MEM is a signed integer big enough to count the total memory +@* used by Lua. +** CHANGE here if for some weird reason the default definitions are not +** good enough for your machine. (The definitions in the 'else' +** part always works, but may waste space on machines with 64-bit +** longs.) Probably you do not need to change this. +*/ +#if LUAI_BITSINT >= 32 +#define LUAI_UINT32 unsigned int +#define LUAI_INT32 int +#define LUAI_MAXINT32 INT_MAX +#define LUAI_UMEM size_t +#define LUAI_MEM ptrdiff_t +#else +/* 16-bit ints */ +#define LUAI_UINT32 unsigned long +#define LUAI_INT32 long +#define LUAI_MAXINT32 LONG_MAX +#define LUAI_UMEM unsigned long +#define LUAI_MEM long +#endif + + +/* +@@ LUAI_MAXCALLS limits the number of nested calls. +** CHANGE it if you need really deep recursive calls. This limit is +** arbitrary; its only purpose is to stop infinite recursion before +** exhausting memory. +*/ +#define LUAI_MAXCALLS 20000 + + +/* +@@ LUAI_MAXCSTACK limits the number of Lua stack slots that a C function +@* can use. +** CHANGE it if you need lots of (Lua) stack space for your C +** functions. This limit is arbitrary; its only purpose is to stop C +** functions to consume unlimited stack space. (must be smaller than +** -LUA_REGISTRYINDEX) +*/ +#define LUAI_MAXCSTACK 8000 + + + +/* +** {================================================================== +** CHANGE (to smaller values) the following definitions if your system +** has a small C stack. (Or you may want to change them to larger +** values if your system has a large C stack and these limits are +** too rigid for you.) Some of these constants control the size of +** stack-allocated arrays used by the compiler or the interpreter, while +** others limit the maximum number of recursive calls that the compiler +** or the interpreter can perform. Values too large may cause a C stack +** overflow for some forms of deep constructs. +** =================================================================== +*/ + + +/* +@@ LUAI_MAXCCALLS is the maximum depth for nested C calls (short) and +@* syntactical nested non-terminals in a program. +*/ +#define LUAI_MAXCCALLS 200 + + +/* +@@ LUAI_MAXVARS is the maximum number of local variables per function +@* (must be smaller than 250). +*/ +#define LUAI_MAXVARS 200 + + +/* +@@ LUAI_MAXUPVALUES is the maximum number of upvalues per function +@* (must be smaller than 250). +*/ +#define LUAI_MAXUPVALUES 60 + + +/* +@@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system. +*/ +#define LUAL_BUFFERSIZE BUFSIZ + +/* }================================================================== */ + + + + +/* +** {================================================================== +@@ LUA_NUMBER is the type of numbers in Lua. +** CHANGE the following definitions only if you want to build Lua +** with a number type different from double. You may also need to +** change lua_number2int & lua_number2integer. +** =================================================================== +*/ + +#define LUA_NUMBER_DOUBLE +#define LUA_NUMBER double + +/* +@@ LUAI_UACNUMBER is the result of an 'usual argument conversion' +@* over a number. +*/ +#define LUAI_UACNUMBER double + + +/* +@@ LUA_NUMBER_SCAN is the format for reading numbers. +@@ LUA_NUMBER_FMT is the format for writing numbers. +@@ lua_number2str converts a number to a string. +@@ LUAI_MAXNUMBER2STR is maximum size of previous conversion. +@@ lua_str2number converts a string to a number. +*/ +#define LUA_NUMBER_SCAN "%lf" +#define LUA_NUMBER_FMT "%.14g" +#define lua_number2str(s,n) sprintf((s), LUA_NUMBER_FMT, (n)) +#define LUAI_MAXNUMBER2STR 32 /* 16 digits, sign, point, and \0 */ +#define lua_str2number(s,p) strtod((s), (p)) + + +/* +@@ The luai_num* macros define the primitive operations over numbers. +*/ +#if defined(LUA_CORE) +#include +#define luai_numadd(a,b) ((a)+(b)) +#define luai_numsub(a,b) ((a)-(b)) +#define luai_nummul(a,b) ((a)*(b)) +#define luai_numdiv(a,b) ((a)/(b)) +#define luai_nummod(a,b) ((a) - floor((a)/(b))*(b)) +#define luai_numpow(a,b) (pow(a,b)) +#define luai_numunm(a) (-(a)) +#define luai_numeq(a,b) ((a)==(b)) +#define luai_numlt(a,b) ((a)<(b)) +#define luai_numle(a,b) ((a)<=(b)) +#define luai_numisnan(a) (!luai_numeq((a), (a))) +#endif + + +/* +@@ lua_number2int is a macro to convert lua_Number to int. +@@ lua_number2integer is a macro to convert lua_Number to lua_Integer. +** CHANGE them if you know a faster way to convert a lua_Number to +** int (with any rounding method and without throwing errors) in your +** system. In Pentium machines, a naive typecast from double to int +** in C is extremely slow, so any alternative is worth trying. +*/ + +/* On a Pentium, resort to a trick */ +#if defined(LUA_NUMBER_DOUBLE) && !defined(LUA_ANSI) && !defined(__SSE2__) && \ + (defined(__i386) || defined (_M_IX86) || defined(__i386__)) + +/* On a Microsoft compiler, use assembler */ +#if defined(_MSC_VER) + +#define lua_number2int(i,d) __asm fld d __asm fistp i +#define lua_number2integer(i,n) lua_number2int(i, n) + +/* the next trick should work on any Pentium, but sometimes clashes + with a DirectX idiosyncrasy */ +#else + +union luai_Cast { double l_d; long l_l; }; +#define lua_number2int(i,d) \ + { volatile union luai_Cast u; u.l_d = (d) + 6755399441055744.0; (i) = u.l_l; } +#define lua_number2integer(i,n) lua_number2int(i, n) + +#endif + + +/* this option always works, but may be slow */ +#else +#define lua_number2int(i,d) ((i)=(int)(d)) +#define lua_number2integer(i,d) ((i)=(lua_Integer)(d)) + +#endif + +/* }================================================================== */ + + +/* +@@ LUAI_USER_ALIGNMENT_T is a type that requires maximum alignment. +** CHANGE it if your system requires alignments larger than double. (For +** instance, if your system supports long doubles and they must be +** aligned in 16-byte boundaries, then you should add long double in the +** union.) Probably you do not need to change this. +*/ +#define LUAI_USER_ALIGNMENT_T union { double u; void *s; long l; } + + +/* +@@ LUAI_THROW/LUAI_TRY define how Lua does exception handling. +** CHANGE them if you prefer to use longjmp/setjmp even with C++ +** or if want/don't to use _longjmp/_setjmp instead of regular +** longjmp/setjmp. By default, Lua handles errors with exceptions when +** compiling as C++ code, with _longjmp/_setjmp when asked to use them, +** and with longjmp/setjmp otherwise. +*/ +#if defined(__cplusplus) +/* C++ exceptions */ +#define LUAI_THROW(L,c) throw(c) +#define LUAI_TRY(L,c,a) try { a } catch(...) \ + { if ((c)->status == 0) (c)->status = -1; } +#define luai_jmpbuf int /* dummy variable */ + +#elif defined(LUA_USE_ULONGJMP) +/* in Unix, try _longjmp/_setjmp (more efficient) */ +#define LUAI_THROW(L,c) _longjmp((c)->b, 1) +#define LUAI_TRY(L,c,a) if (_setjmp((c)->b) == 0) { a } +#define luai_jmpbuf jmp_buf + +#else +/* default handling with long jumps */ +#define LUAI_THROW(L,c) longjmp((c)->b, 1) +#define LUAI_TRY(L,c,a) if (setjmp((c)->b) == 0) { a } +#define luai_jmpbuf jmp_buf + +#endif + + +/* +@@ LUA_MAXCAPTURES is the maximum number of captures that a pattern +@* can do during pattern-matching. +** CHANGE it if you need more captures. This limit is arbitrary. +*/ +#define LUA_MAXCAPTURES 32 + + +/* +@@ lua_tmpnam is the function that the OS library uses to create a +@* temporary name. +@@ LUA_TMPNAMBUFSIZE is the maximum size of a name created by lua_tmpnam. +** CHANGE them if you have an alternative to tmpnam (which is considered +** insecure) or if you want the original tmpnam anyway. By default, Lua +** uses tmpnam except when POSIX is available, where it uses mkstemp. +*/ +#if defined(loslib_c) || defined(luaall_c) + +#if defined(LUA_USE_MKSTEMP) +#include +#define LUA_TMPNAMBUFSIZE 32 +#define lua_tmpnam(b,e) { \ + strcpy(b, "/tmp/lua_XXXXXX"); \ + e = mkstemp(b); \ + if (e != -1) close(e); \ + e = (e == -1); } + +#else +#define LUA_TMPNAMBUFSIZE L_tmpnam +#define lua_tmpnam(b,e) { e = (tmpnam(b) == NULL); } +#endif + +#endif + + +/* +@@ lua_popen spawns a new process connected to the current one through +@* the file streams. +** CHANGE it if you have a way to implement it in your system. +*/ +#if defined(LUA_USE_POPEN) + +#define lua_popen(L,c,m) ((void)L, fflush(NULL), popen(c,m)) +#define lua_pclose(L,file) ((void)L, (pclose(file) != -1)) + +#elif defined(LUA_WIN) + +#define lua_popen(L,c,m) ((void)L, _popen(c,m)) +#define lua_pclose(L,file) ((void)L, (_pclose(file) != -1)) + +#else + +#define lua_popen(L,c,m) ((void)((void)c, m), \ + luaL_error(L, LUA_QL("popen") " not supported"), (FILE*)0) +#define lua_pclose(L,file) ((void)((void)L, file), 0) + +#endif + +/* +@@ LUA_DL_* define which dynamic-library system Lua should use. +** CHANGE here if Lua has problems choosing the appropriate +** dynamic-library system for your platform (either Windows' DLL, Mac's +** dyld, or Unix's dlopen). If your system is some kind of Unix, there +** is a good chance that it has dlopen, so LUA_DL_DLOPEN will work for +** it. To use dlopen you also need to adapt the src/Makefile (probably +** adding -ldl to the linker options), so Lua does not select it +** automatically. (When you change the makefile to add -ldl, you must +** also add -DLUA_USE_DLOPEN.) +** If you do not want any kind of dynamic library, undefine all these +** options. +** By default, _WIN32 gets LUA_DL_DLL and MAC OS X gets LUA_DL_DYLD. +*/ +#if defined(LUA_USE_DLOPEN) +#define LUA_DL_DLOPEN +#endif + +#if defined(LUA_WIN) +#define LUA_DL_DLL +#endif + + +/* +@@ LUAI_EXTRASPACE allows you to add user-specific data in a lua_State +@* (the data goes just *before* the lua_State pointer). +** CHANGE (define) this if you really need that. This value must be +** a multiple of the maximum alignment required for your machine. +*/ +#define LUAI_EXTRASPACE 0 + + +/* +@@ luai_userstate* allow user-specific actions on threads. +** CHANGE them if you defined LUAI_EXTRASPACE and need to do something +** extra when a thread is created/deleted/resumed/yielded. +*/ +#define luai_userstateopen(L) ((void)L) +#define luai_userstateclose(L) ((void)L) +#define luai_userstatethread(L,L1) ((void)L) +#define luai_userstatefree(L) ((void)L) +#define luai_userstateresume(L,n) ((void)L) +#define luai_userstateyield(L,n) ((void)L) + + +/* +@@ LUA_INTFRMLEN is the length modifier for integer conversions +@* in 'string.format'. +@@ LUA_INTFRM_T is the integer type correspoding to the previous length +@* modifier. +** CHANGE them if your system supports long long or does not support long. +*/ + +#if defined(LUA_USELONGLONG) + +#define LUA_INTFRMLEN "ll" +#define LUA_INTFRM_T long long + +#else + +#define LUA_INTFRMLEN "l" +#define LUA_INTFRM_T long + +#endif + + + +/* =================================================================== */ + +/* +** Local configuration. You can use this space to add your redefinitions +** without modifying the main part of the file. +*/ + + + +#endif + diff --git a/lib/lua/lualib.h b/lib/lua/lualib.h new file mode 100644 index 000000000000..469417f6703e --- /dev/null +++ b/lib/lua/lualib.h @@ -0,0 +1,53 @@ +/* +** $Id: lualib.h,v 1.36.1.1 2007/12/27 13:02:25 roberto Exp $ +** Lua standard libraries +** See Copyright Notice in lua.h +*/ + + +#ifndef lualib_h +#define lualib_h + +#include "lua.h" + + +/* Key to file-handle type */ +#define LUA_FILEHANDLE "FILE*" + + +#define LUA_COLIBNAME "coroutine" +LUALIB_API int (luaopen_base) (lua_State *L); + +#define LUA_TABLIBNAME "table" +LUALIB_API int (luaopen_table) (lua_State *L); + +#define LUA_IOLIBNAME "io" +LUALIB_API int (luaopen_io) (lua_State *L); + +#define LUA_OSLIBNAME "os" +LUALIB_API int (luaopen_os) (lua_State *L); + +#define LUA_STRLIBNAME "string" +LUALIB_API int (luaopen_string) (lua_State *L); + +#define LUA_MATHLIBNAME "math" +LUALIB_API int (luaopen_math) (lua_State *L); + +#define LUA_DBLIBNAME "debug" +LUALIB_API int (luaopen_debug) (lua_State *L); + +#define LUA_LOADLIBNAME "package" +LUALIB_API int (luaopen_package) (lua_State *L); + + +/* open all previous libraries */ +LUALIB_API void (luaL_openlibs) (lua_State *L); + + + +#ifndef lua_assert +#define lua_assert(x) ((void)0) +#endif + + +#endif diff --git a/lib/lua/lundump.c b/lib/lua/lundump.c new file mode 100644 index 000000000000..8010a45795ba --- /dev/null +++ b/lib/lua/lundump.c @@ -0,0 +1,227 @@ +/* +** $Id: lundump.c,v 2.7.1.4 2008/04/04 19:51:41 roberto Exp $ +** load precompiled Lua chunks +** See Copyright Notice in lua.h +*/ + +#include + +#define lundump_c +#define LUA_CORE + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstring.h" +#include "lundump.h" +#include "lzio.h" + +typedef struct { + lua_State* L; + ZIO* Z; + Mbuffer* b; + const char* name; +} LoadState; + +#ifdef LUAC_TRUST_BINARIES +#define IF(c,s) +#define error(S,s) +#else +#define IF(c,s) if (c) error(S,s) + +static void error(LoadState* S, const char* why) +{ + luaO_pushfstring(S->L,"%s: %s in precompiled chunk",S->name,why); + luaD_throw(S->L,LUA_ERRSYNTAX); +} +#endif + +#define LoadMem(S,b,n,size) LoadBlock(S,b,(n)*(size)) +#define LoadByte(S) (lu_byte)LoadChar(S) +#define LoadVar(S,x) LoadMem(S,&x,1,sizeof(x)) +#define LoadVector(S,b,n,size) LoadMem(S,b,n,size) + +static void LoadBlock(LoadState* S, void* b, size_t size) +{ + size_t r=luaZ_read(S->Z,b,size); + IF (r!=0, "unexpected end"); +} + +static int LoadChar(LoadState* S) +{ + char x; + LoadVar(S,x); + return x; +} + +static int LoadInt(LoadState* S) +{ + int x; + LoadVar(S,x); + IF (x<0, "bad integer"); + return x; +} + +static lua_Number LoadNumber(LoadState* S) +{ + lua_Number x; + LoadVar(S,x); + return x; +} + +static TString* LoadString(LoadState* S) +{ + size_t size; + LoadVar(S,size); + if (size==0) + return NULL; + else + { + char* s=luaZ_openspace(S->L,S->b,size); + LoadBlock(S,s,size); + return luaS_newlstr(S->L,s,size-1); /* remove trailing '\0' */ + } +} + +static void LoadCode(LoadState* S, Proto* f) +{ + int n=LoadInt(S); + f->code=luaM_newvector(S->L,n,Instruction); + f->sizecode=n; + LoadVector(S,f->code,n,sizeof(Instruction)); +} + +static Proto* LoadFunction(LoadState* S, TString* p); + +static void LoadConstants(LoadState* S, Proto* f) +{ + int i,n; + n=LoadInt(S); + f->k=luaM_newvector(S->L,n,TValue); + f->sizek=n; + for (i=0; ik[i]); + for (i=0; ik[i]; + int t=LoadChar(S); + switch (t) + { + case LUA_TNIL: + setnilvalue(o); + break; + case LUA_TBOOLEAN: + setbvalue(o,LoadChar(S)!=0); + break; + case LUA_TNUMBER: + setnvalue(o,LoadNumber(S)); + break; + case LUA_TSTRING: + setsvalue2n(S->L,o,LoadString(S)); + break; + default: + error(S,"bad constant"); + break; + } + } + n=LoadInt(S); + f->p=luaM_newvector(S->L,n,Proto*); + f->sizep=n; + for (i=0; ip[i]=NULL; + for (i=0; ip[i]=LoadFunction(S,f->source); +} + +static void LoadDebug(LoadState* S, Proto* f) +{ + int i,n; + n=LoadInt(S); + f->lineinfo=luaM_newvector(S->L,n,int); + f->sizelineinfo=n; + LoadVector(S,f->lineinfo,n,sizeof(int)); + n=LoadInt(S); + f->locvars=luaM_newvector(S->L,n,LocVar); + f->sizelocvars=n; + for (i=0; ilocvars[i].varname=NULL; + for (i=0; ilocvars[i].varname=LoadString(S); + f->locvars[i].startpc=LoadInt(S); + f->locvars[i].endpc=LoadInt(S); + } + n=LoadInt(S); + f->upvalues=luaM_newvector(S->L,n,TString*); + f->sizeupvalues=n; + for (i=0; iupvalues[i]=NULL; + for (i=0; iupvalues[i]=LoadString(S); +} + +static Proto* LoadFunction(LoadState* S, TString* p) +{ + Proto* f; + if (++S->L->nCcalls > LUAI_MAXCCALLS) error(S,"code too deep"); + f=luaF_newproto(S->L); + setptvalue2s(S->L,S->L->top,f); incr_top(S->L); + f->source=LoadString(S); if (f->source==NULL) f->source=p; + f->linedefined=LoadInt(S); + f->lastlinedefined=LoadInt(S); + f->nups=LoadByte(S); + f->numparams=LoadByte(S); + f->is_vararg=LoadByte(S); + f->maxstacksize=LoadByte(S); + LoadCode(S,f); + LoadConstants(S,f); + LoadDebug(S,f); + IF (!luaG_checkcode(f), "bad code"); + S->L->top--; + S->L->nCcalls--; + return f; +} + +static void LoadHeader(LoadState* S) +{ + char h[LUAC_HEADERSIZE]; + char s[LUAC_HEADERSIZE]; + luaU_header(h); + LoadBlock(S,s,LUAC_HEADERSIZE); + IF (memcmp(h,s,LUAC_HEADERSIZE)!=0, "bad header"); +} + +/* +** load precompiled chunk +*/ +Proto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name) +{ + LoadState S; + if (*name=='@' || *name=='=') + S.name=name+1; + else if (*name==LUA_SIGNATURE[0]) + S.name="binary string"; + else + S.name=name; + S.L=L; + S.Z=Z; + S.b=buff; + LoadHeader(&S); + return LoadFunction(&S,luaS_newliteral(L,"=?")); +} + +/* +* make header +*/ +void luaU_header (char* h) +{ + int x=1; + memcpy(h,LUA_SIGNATURE,sizeof(LUA_SIGNATURE)-1); + h+=sizeof(LUA_SIGNATURE)-1; + *h++=(char)LUAC_VERSION; + *h++=(char)LUAC_FORMAT; + *h++=(char)*(char*)&x; /* endianness */ + *h++=(char)sizeof(int); + *h++=(char)sizeof(size_t); + *h++=(char)sizeof(Instruction); + *h++=(char)sizeof(lua_Number); + *h++=(char)(((lua_Number)0.5)==0); /* is lua_Number integral? */ +} diff --git a/lib/lua/lundump.h b/lib/lua/lundump.h new file mode 100644 index 000000000000..c80189dbffc6 --- /dev/null +++ b/lib/lua/lundump.h @@ -0,0 +1,36 @@ +/* +** $Id: lundump.h,v 1.37.1.1 2007/12/27 13:02:25 roberto Exp $ +** load precompiled Lua chunks +** See Copyright Notice in lua.h +*/ + +#ifndef lundump_h +#define lundump_h + +#include "lobject.h" +#include "lzio.h" + +/* load one chunk; from lundump.c */ +LUAI_FUNC Proto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name); + +/* make header; from lundump.c */ +LUAI_FUNC void luaU_header (char* h); + +/* dump one chunk; from ldump.c */ +LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip); + +#ifdef luac_c +/* print one chunk; from print.c */ +LUAI_FUNC void luaU_print (const Proto* f, int full); +#endif + +/* for header of binary files -- this is Lua 5.1 */ +#define LUAC_VERSION 0x51 + +/* for header of binary files -- this is the official format */ +#define LUAC_FORMAT 0 + +/* size of header of binary files */ +#define LUAC_HEADERSIZE 12 + +#endif diff --git a/lib/lua/lvm.c b/lib/lua/lvm.c new file mode 100644 index 000000000000..e0a0cd85219d --- /dev/null +++ b/lib/lua/lvm.c @@ -0,0 +1,767 @@ +/* +** $Id: lvm.c,v 2.63.1.5 2011/08/17 20:43:11 roberto Exp $ +** Lua virtual machine +** See Copyright Notice in lua.h +*/ + + +#include +#include +#include + +#define lvm_c +#define LUA_CORE + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" +#include "lvm.h" + + + +/* limit for table tag-method chains (to avoid loops) */ +#define MAXTAGLOOP 100 + + +const TValue *luaV_tonumber (const TValue *obj, TValue *n) { + lua_Number num; + if (ttisnumber(obj)) return obj; + if (ttisstring(obj) && luaO_str2d(svalue(obj), &num)) { + setnvalue(n, num); + return n; + } + else + return NULL; +} + + +int luaV_tostring (lua_State *L, StkId obj) { + if (!ttisnumber(obj)) + return 0; + else { + char s[LUAI_MAXNUMBER2STR]; + lua_Number n = nvalue(obj); + lua_number2str(s, n); + setsvalue2s(L, obj, luaS_new(L, s)); + return 1; + } +} + + +static void traceexec (lua_State *L, const Instruction *pc) { + lu_byte mask = L->hookmask; + const Instruction *oldpc = L->savedpc; + L->savedpc = pc; + if ((mask & LUA_MASKCOUNT) && L->hookcount == 0) { + resethookcount(L); + luaD_callhook(L, LUA_HOOKCOUNT, -1); + } + if (mask & LUA_MASKLINE) { + Proto *p = ci_func(L->ci)->l.p; + int npc = pcRel(pc, p); + int newline = getline(p, npc); + /* call linehook when enter a new function, when jump back (loop), + or when enter a new line */ + if (npc == 0 || pc <= oldpc || newline != getline(p, pcRel(oldpc, p))) + luaD_callhook(L, LUA_HOOKLINE, newline); + } +} + + +static void callTMres (lua_State *L, StkId res, const TValue *f, + const TValue *p1, const TValue *p2) { + ptrdiff_t result = savestack(L, res); + setobj2s(L, L->top, f); /* push function */ + setobj2s(L, L->top+1, p1); /* 1st argument */ + setobj2s(L, L->top+2, p2); /* 2nd argument */ + luaD_checkstack(L, 3); + L->top += 3; + luaD_call(L, L->top - 3, 1); + res = restorestack(L, result); + L->top--; + setobjs2s(L, res, L->top); +} + + + +static void callTM (lua_State *L, const TValue *f, const TValue *p1, + const TValue *p2, const TValue *p3) { + setobj2s(L, L->top, f); /* push function */ + setobj2s(L, L->top+1, p1); /* 1st argument */ + setobj2s(L, L->top+2, p2); /* 2nd argument */ + setobj2s(L, L->top+3, p3); /* 3th argument */ + luaD_checkstack(L, 4); + L->top += 4; + luaD_call(L, L->top - 4, 0); +} + + +void luaV_gettable (lua_State *L, const TValue *t, TValue *key, StkId val) { + int loop; + for (loop = 0; loop < MAXTAGLOOP; loop++) { + const TValue *tm; + if (ttistable(t)) { /* `t' is a table? */ + Table *h = hvalue(t); + const TValue *res = luaH_get(h, key); /* do a primitive get */ + if (!ttisnil(res) || /* result is no nil? */ + (tm = fasttm(L, h->metatable, TM_INDEX)) == NULL) { /* or no TM? */ + setobj2s(L, val, res); + return; + } + /* else will try the tag method */ + } + else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX))) + luaG_typeerror(L, t, "index"); + if (ttisfunction(tm)) { + callTMres(L, val, tm, t, key); + return; + } + t = tm; /* else repeat with `tm' */ + } + luaG_runerror(L, "loop in gettable"); +} + + +void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) { + int loop; + TValue temp; + for (loop = 0; loop < MAXTAGLOOP; loop++) { + const TValue *tm; + if (ttistable(t)) { /* `t' is a table? */ + Table *h = hvalue(t); + TValue *oldval = luaH_set(L, h, key); /* do a primitive set */ + if (!ttisnil(oldval) || /* result is no nil? */ + (tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL) { /* or no TM? */ + setobj2t(L, oldval, val); + h->flags = 0; + luaC_barriert(L, h, val); + return; + } + /* else will try the tag method */ + } + else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX))) + luaG_typeerror(L, t, "index"); + if (ttisfunction(tm)) { + callTM(L, tm, t, key, val); + return; + } + /* else repeat with `tm' */ + setobj(L, &temp, tm); /* avoid pointing inside table (may rehash) */ + t = &temp; + } + luaG_runerror(L, "loop in settable"); +} + + +static int call_binTM (lua_State *L, const TValue *p1, const TValue *p2, + StkId res, TMS event) { + const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */ + if (ttisnil(tm)) + tm = luaT_gettmbyobj(L, p2, event); /* try second operand */ + if (ttisnil(tm)) return 0; + callTMres(L, res, tm, p1, p2); + return 1; +} + + +static const TValue *get_compTM (lua_State *L, Table *mt1, Table *mt2, + TMS event) { + const TValue *tm1 = fasttm(L, mt1, event); + const TValue *tm2; + if (tm1 == NULL) return NULL; /* no metamethod */ + if (mt1 == mt2) return tm1; /* same metatables => same metamethods */ + tm2 = fasttm(L, mt2, event); + if (tm2 == NULL) return NULL; /* no metamethod */ + if (luaO_rawequalObj(tm1, tm2)) /* same metamethods? */ + return tm1; + return NULL; +} + + +static int call_orderTM (lua_State *L, const TValue *p1, const TValue *p2, + TMS event) { + const TValue *tm1 = luaT_gettmbyobj(L, p1, event); + const TValue *tm2; + if (ttisnil(tm1)) return -1; /* no metamethod? */ + tm2 = luaT_gettmbyobj(L, p2, event); + if (!luaO_rawequalObj(tm1, tm2)) /* different metamethods? */ + return -1; + callTMres(L, L->top, tm1, p1, p2); + return !l_isfalse(L->top); +} + + +static int l_strcmp (const TString *ls, const TString *rs) { + const char *l = getstr(ls); + size_t ll = ls->tsv.len; + const char *r = getstr(rs); + size_t lr = rs->tsv.len; + for (;;) { + int temp = strcoll(l, r); + if (temp != 0) return temp; + else { /* strings are equal up to a `\0' */ + size_t len = strlen(l); /* index of first `\0' in both strings */ + if (len == lr) /* r is finished? */ + return (len == ll) ? 0 : 1; + else if (len == ll) /* l is finished? */ + return -1; /* l is smaller than r (because r is not finished) */ + /* both strings longer than `len'; go on comparing (after the `\0') */ + len++; + l += len; ll -= len; r += len; lr -= len; + } + } +} + + +int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) { + int res; + if (ttype(l) != ttype(r)) + return luaG_ordererror(L, l, r); + else if (ttisnumber(l)) + return luai_numlt(nvalue(l), nvalue(r)); + else if (ttisstring(l)) + return l_strcmp(rawtsvalue(l), rawtsvalue(r)) < 0; + else if ((res = call_orderTM(L, l, r, TM_LT)) != -1) + return res; + return luaG_ordererror(L, l, r); +} + + +static int lessequal (lua_State *L, const TValue *l, const TValue *r) { + int res; + if (ttype(l) != ttype(r)) + return luaG_ordererror(L, l, r); + else if (ttisnumber(l)) + return luai_numle(nvalue(l), nvalue(r)); + else if (ttisstring(l)) + return l_strcmp(rawtsvalue(l), rawtsvalue(r)) <= 0; + else if ((res = call_orderTM(L, l, r, TM_LE)) != -1) /* first try `le' */ + return res; + else if ((res = call_orderTM(L, r, l, TM_LT)) != -1) /* else try `lt' */ + return !res; + return luaG_ordererror(L, l, r); +} + + +int luaV_equalval (lua_State *L, const TValue *t1, const TValue *t2) { + const TValue *tm; + lua_assert(ttype(t1) == ttype(t2)); + switch (ttype(t1)) { + case LUA_TNIL: return 1; + case LUA_TNUMBER: return luai_numeq(nvalue(t1), nvalue(t2)); + case LUA_TBOOLEAN: return bvalue(t1) == bvalue(t2); /* true must be 1 !! */ + case LUA_TLIGHTUSERDATA: return pvalue(t1) == pvalue(t2); + case LUA_TUSERDATA: { + if (uvalue(t1) == uvalue(t2)) return 1; + tm = get_compTM(L, uvalue(t1)->metatable, uvalue(t2)->metatable, + TM_EQ); + break; /* will try TM */ + } + case LUA_TTABLE: { + if (hvalue(t1) == hvalue(t2)) return 1; + tm = get_compTM(L, hvalue(t1)->metatable, hvalue(t2)->metatable, TM_EQ); + break; /* will try TM */ + } + default: return gcvalue(t1) == gcvalue(t2); + } + if (tm == NULL) return 0; /* no TM? */ + callTMres(L, L->top, tm, t1, t2); /* call TM */ + return !l_isfalse(L->top); +} + + +void luaV_concat (lua_State *L, int total, int last) { + do { + StkId top = L->base + last + 1; + int n = 2; /* number of elements handled in this pass (at least 2) */ + if (!(ttisstring(top-2) || ttisnumber(top-2)) || !tostring(L, top-1)) { + if (!call_binTM(L, top-2, top-1, top-2, TM_CONCAT)) + luaG_concaterror(L, top-2, top-1); + } else if (tsvalue(top-1)->len == 0) /* second op is empty? */ + (void)tostring(L, top - 2); /* result is first op (as string) */ + else { + /* at least two string values; get as many as possible */ + size_t tl = tsvalue(top-1)->len; + char *buffer; + int i; + /* collect total length */ + for (n = 1; n < total && tostring(L, top-n-1); n++) { + size_t l = tsvalue(top-n-1)->len; + if (l >= MAX_SIZET - tl) luaG_runerror(L, "string length overflow"); + tl += l; + } + buffer = luaZ_openspace(L, &G(L)->buff, tl); + tl = 0; + for (i=n; i>0; i--) { /* concat all strings */ + size_t l = tsvalue(top-i)->len; + memcpy(buffer+tl, svalue(top-i), l); + tl += l; + } + setsvalue2s(L, top-n, luaS_newlstr(L, buffer, tl)); + } + total -= n-1; /* got `n' strings to create 1 new */ + last -= n-1; + } while (total > 1); /* repeat until only 1 result left */ +} + + +static void Arith (lua_State *L, StkId ra, const TValue *rb, + const TValue *rc, TMS op) { + TValue tempb, tempc; + const TValue *b, *c; + if ((b = luaV_tonumber(rb, &tempb)) != NULL && + (c = luaV_tonumber(rc, &tempc)) != NULL) { + lua_Number nb = nvalue(b), nc = nvalue(c); + switch (op) { + case TM_ADD: setnvalue(ra, luai_numadd(nb, nc)); break; + case TM_SUB: setnvalue(ra, luai_numsub(nb, nc)); break; + case TM_MUL: setnvalue(ra, luai_nummul(nb, nc)); break; + case TM_DIV: setnvalue(ra, luai_numdiv(nb, nc)); break; + case TM_MOD: setnvalue(ra, luai_nummod(nb, nc)); break; + case TM_POW: setnvalue(ra, luai_numpow(nb, nc)); break; + case TM_UNM: setnvalue(ra, luai_numunm(nb)); break; + default: lua_assert(0); break; + } + } + else if (!call_binTM(L, rb, rc, ra, op)) + luaG_aritherror(L, rb, rc); +} + + + +/* +** some macros for common tasks in `luaV_execute' +*/ + +#define runtime_check(L, c) { if (!(c)) break; } + +#define RA(i) (base+GETARG_A(i)) +/* to be used after possible stack reallocation */ +#define RB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgR, base+GETARG_B(i)) +#define RC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgR, base+GETARG_C(i)) +#define RKB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgK, \ + ISK(GETARG_B(i)) ? k+INDEXK(GETARG_B(i)) : base+GETARG_B(i)) +#define RKC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgK, \ + ISK(GETARG_C(i)) ? k+INDEXK(GETARG_C(i)) : base+GETARG_C(i)) +#define KBx(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgK, k+GETARG_Bx(i)) + + +#define dojump(L,pc,i) {(pc) += (i); luai_threadyield(L);} + + +#define Protect(x) { L->savedpc = pc; {x;}; base = L->base; } + + +#define arith_op(op,tm) { \ + TValue *rb = RKB(i); \ + TValue *rc = RKC(i); \ + if (ttisnumber(rb) && ttisnumber(rc)) { \ + lua_Number nb = nvalue(rb), nc = nvalue(rc); \ + setnvalue(ra, op(nb, nc)); \ + } \ + else \ + Protect(Arith(L, ra, rb, rc, tm)); \ + } + + + +void luaV_execute (lua_State *L, int nexeccalls) { + LClosure *cl; + StkId base; + TValue *k; + const Instruction *pc; + reentry: /* entry point */ + lua_assert(isLua(L->ci)); + pc = L->savedpc; + cl = &clvalue(L->ci->func)->l; + base = L->base; + k = cl->p->k; + /* main loop of interpreter */ + for (;;) { + const Instruction i = *pc++; + StkId ra; + if ((L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) && + (--L->hookcount == 0 || L->hookmask & LUA_MASKLINE)) { + traceexec(L, pc); + if (L->status == LUA_YIELD) { /* did hook yield? */ + L->savedpc = pc - 1; + return; + } + base = L->base; + } + /* warning!! several calls may realloc the stack and invalidate `ra' */ + ra = RA(i); + lua_assert(base == L->base && L->base == L->ci->base); + lua_assert(base <= L->top && L->top <= L->stack + L->stacksize); + lua_assert(L->top == L->ci->top || luaG_checkopenop(i)); + switch (GET_OPCODE(i)) { + case OP_MOVE: { + setobjs2s(L, ra, RB(i)); + continue; + } + case OP_LOADK: { + setobj2s(L, ra, KBx(i)); + continue; + } + case OP_LOADBOOL: { + setbvalue(ra, GETARG_B(i)); + if (GETARG_C(i)) pc++; /* skip next instruction (if C) */ + continue; + } + case OP_LOADNIL: { + TValue *rb = RB(i); + do { + setnilvalue(rb--); + } while (rb >= ra); + continue; + } + case OP_GETUPVAL: { + int b = GETARG_B(i); + setobj2s(L, ra, cl->upvals[b]->v); + continue; + } + case OP_GETGLOBAL: { + TValue g; + TValue *rb = KBx(i); + sethvalue(L, &g, cl->env); + lua_assert(ttisstring(rb)); + Protect(luaV_gettable(L, &g, rb, ra)); + continue; + } + case OP_GETTABLE: { + Protect(luaV_gettable(L, RB(i), RKC(i), ra)); + continue; + } + case OP_SETGLOBAL: { + TValue g; + sethvalue(L, &g, cl->env); + lua_assert(ttisstring(KBx(i))); + Protect(luaV_settable(L, &g, KBx(i), ra)); + continue; + } + case OP_SETUPVAL: { + UpVal *uv = cl->upvals[GETARG_B(i)]; + setobj(L, uv->v, ra); + luaC_barrier(L, uv, ra); + continue; + } + case OP_SETTABLE: { + Protect(luaV_settable(L, ra, RKB(i), RKC(i))); + continue; + } + case OP_NEWTABLE: { + int b = GETARG_B(i); + int c = GETARG_C(i); + sethvalue(L, ra, luaH_new(L, luaO_fb2int(b), luaO_fb2int(c))); + Protect(luaC_checkGC(L)); + continue; + } + case OP_SELF: { + StkId rb = RB(i); + setobjs2s(L, ra+1, rb); + Protect(luaV_gettable(L, rb, RKC(i), ra)); + continue; + } + case OP_ADD: { + arith_op(luai_numadd, TM_ADD); + continue; + } + case OP_SUB: { + arith_op(luai_numsub, TM_SUB); + continue; + } + case OP_MUL: { + arith_op(luai_nummul, TM_MUL); + continue; + } + case OP_DIV: { + arith_op(luai_numdiv, TM_DIV); + continue; + } + case OP_MOD: { + arith_op(luai_nummod, TM_MOD); + continue; + } + case OP_POW: { + arith_op(luai_numpow, TM_POW); + continue; + } + case OP_UNM: { + TValue *rb = RB(i); + if (ttisnumber(rb)) { + lua_Number nb = nvalue(rb); + setnvalue(ra, luai_numunm(nb)); + } + else { + Protect(Arith(L, ra, rb, rb, TM_UNM)); + } + continue; + } + case OP_NOT: { + int res = l_isfalse(RB(i)); /* next assignment may change this value */ + setbvalue(ra, res); + continue; + } + case OP_LEN: { + const TValue *rb = RB(i); + switch (ttype(rb)) { + case LUA_TTABLE: { + setnvalue(ra, cast_num(luaH_getn(hvalue(rb)))); + break; + } + case LUA_TSTRING: { + setnvalue(ra, cast_num(tsvalue(rb)->len)); + break; + } + default: { /* try metamethod */ + Protect( + if (!call_binTM(L, rb, luaO_nilobject, ra, TM_LEN)) + luaG_typeerror(L, rb, "get length of"); + ) + } + } + continue; + } + case OP_CONCAT: { + int b = GETARG_B(i); + int c = GETARG_C(i); + Protect(luaV_concat(L, c-b+1, c); luaC_checkGC(L)); + setobjs2s(L, RA(i), base+b); + continue; + } + case OP_JMP: { + dojump(L, pc, GETARG_sBx(i)); + continue; + } + case OP_EQ: { + TValue *rb = RKB(i); + TValue *rc = RKC(i); + Protect( + if (equalobj(L, rb, rc) == GETARG_A(i)) + dojump(L, pc, GETARG_sBx(*pc)); + ) + pc++; + continue; + } + case OP_LT: { + Protect( + if (luaV_lessthan(L, RKB(i), RKC(i)) == GETARG_A(i)) + dojump(L, pc, GETARG_sBx(*pc)); + ) + pc++; + continue; + } + case OP_LE: { + Protect( + if (lessequal(L, RKB(i), RKC(i)) == GETARG_A(i)) + dojump(L, pc, GETARG_sBx(*pc)); + ) + pc++; + continue; + } + case OP_TEST: { + if (l_isfalse(ra) != GETARG_C(i)) + dojump(L, pc, GETARG_sBx(*pc)); + pc++; + continue; + } + case OP_TESTSET: { + TValue *rb = RB(i); + if (l_isfalse(rb) != GETARG_C(i)) { + setobjs2s(L, ra, rb); + dojump(L, pc, GETARG_sBx(*pc)); + } + pc++; + continue; + } + case OP_CALL: { + int b = GETARG_B(i); + int nresults = GETARG_C(i) - 1; + if (b != 0) L->top = ra+b; /* else previous instruction set top */ + L->savedpc = pc; + switch (luaD_precall(L, ra, nresults)) { + case PCRLUA: { + nexeccalls++; + goto reentry; /* restart luaV_execute over new Lua function */ + } + case PCRC: { + /* it was a C function (`precall' called it); adjust results */ + if (nresults >= 0) L->top = L->ci->top; + base = L->base; + continue; + } + default: { + return; /* yield */ + } + } + } + case OP_TAILCALL: { + int b = GETARG_B(i); + if (b != 0) L->top = ra+b; /* else previous instruction set top */ + L->savedpc = pc; + lua_assert(GETARG_C(i) - 1 == LUA_MULTRET); + switch (luaD_precall(L, ra, LUA_MULTRET)) { + case PCRLUA: { + /* tail call: put new frame in place of previous one */ + CallInfo *ci = L->ci - 1; /* previous frame */ + int aux; + StkId func = ci->func; + StkId pfunc = (ci+1)->func; /* previous function index */ + if (L->openupval) luaF_close(L, ci->base); + L->base = ci->base = ci->func + ((ci+1)->base - pfunc); + for (aux = 0; pfunc+aux < L->top; aux++) /* move frame down */ + setobjs2s(L, func+aux, pfunc+aux); + ci->top = L->top = func+aux; /* correct top */ + lua_assert(L->top == L->base + clvalue(func)->l.p->maxstacksize); + ci->savedpc = L->savedpc; + ci->tailcalls++; /* one more call lost */ + L->ci--; /* remove new frame */ + goto reentry; + } + case PCRC: { /* it was a C function (`precall' called it) */ + base = L->base; + continue; + } + default: { + return; /* yield */ + } + } + } + case OP_RETURN: { + int b = GETARG_B(i); + if (b != 0) L->top = ra+b-1; + if (L->openupval) luaF_close(L, base); + L->savedpc = pc; + b = luaD_poscall(L, ra); + if (--nexeccalls == 0) /* was previous function running `here'? */ + return; /* no: return */ + else { /* yes: continue its execution */ + if (b) L->top = L->ci->top; + lua_assert(isLua(L->ci)); + lua_assert(GET_OPCODE(*((L->ci)->savedpc - 1)) == OP_CALL); + goto reentry; + } + } + case OP_FORLOOP: { + lua_Number step = nvalue(ra+2); + lua_Number idx = luai_numadd(nvalue(ra), step); /* increment index */ + lua_Number limit = nvalue(ra+1); + if (luai_numlt(0, step) ? luai_numle(idx, limit) + : luai_numle(limit, idx)) { + dojump(L, pc, GETARG_sBx(i)); /* jump back */ + setnvalue(ra, idx); /* update internal index... */ + setnvalue(ra+3, idx); /* ...and external index */ + } + continue; + } + case OP_FORPREP: { + const TValue *init = ra; + const TValue *plimit = ra+1; + const TValue *pstep = ra+2; + L->savedpc = pc; /* next steps may throw errors */ + if (!tonumber(init, ra)) + luaG_runerror(L, LUA_QL("for") " initial value must be a number"); + else if (!tonumber(plimit, ra+1)) + luaG_runerror(L, LUA_QL("for") " limit must be a number"); + else if (!tonumber(pstep, ra+2)) + luaG_runerror(L, LUA_QL("for") " step must be a number"); + setnvalue(ra, luai_numsub(nvalue(ra), nvalue(pstep))); + dojump(L, pc, GETARG_sBx(i)); + continue; + } + case OP_TFORLOOP: { + StkId cb = ra + 3; /* call base */ + setobjs2s(L, cb+2, ra+2); + setobjs2s(L, cb+1, ra+1); + setobjs2s(L, cb, ra); + L->top = cb+3; /* func. + 2 args (state and index) */ + Protect(luaD_call(L, cb, GETARG_C(i))); + L->top = L->ci->top; + cb = RA(i) + 3; /* previous call may change the stack */ + if (!ttisnil(cb)) { /* continue loop? */ + setobjs2s(L, cb-1, cb); /* save control variable */ + dojump(L, pc, GETARG_sBx(*pc)); /* jump back */ + } + pc++; + continue; + } + case OP_SETLIST: { + int n = GETARG_B(i); + int c = GETARG_C(i); + int last; + Table *h; + if (n == 0) { + n = cast_int(L->top - ra) - 1; + L->top = L->ci->top; + } + if (c == 0) c = cast_int(*pc++); + runtime_check(L, ttistable(ra)); + h = hvalue(ra); + last = ((c-1)*LFIELDS_PER_FLUSH) + n; + if (last > h->sizearray) /* needs more space? */ + luaH_resizearray(L, h, last); /* pre-alloc it at once */ + for (; n > 0; n--) { + TValue *val = ra+n; + setobj2t(L, luaH_setnum(L, h, last--), val); + luaC_barriert(L, h, val); + } + continue; + } + case OP_CLOSE: { + luaF_close(L, ra); + continue; + } + case OP_CLOSURE: { + Proto *p; + Closure *ncl; + int nup, j; + p = cl->p->p[GETARG_Bx(i)]; + nup = p->nups; + ncl = luaF_newLclosure(L, nup, cl->env); + ncl->l.p = p; + for (j=0; jl.upvals[j] = cl->upvals[GETARG_B(*pc)]; + else { + lua_assert(GET_OPCODE(*pc) == OP_MOVE); + ncl->l.upvals[j] = luaF_findupval(L, base + GETARG_B(*pc)); + } + } + setclvalue(L, ra, ncl); + Protect(luaC_checkGC(L)); + continue; + } + case OP_VARARG: { + int b = GETARG_B(i) - 1; + int j; + CallInfo *ci = L->ci; + int n = cast_int(ci->base - ci->func) - cl->p->numparams - 1; + if (b == LUA_MULTRET) { + Protect(luaD_checkstack(L, n)); + ra = RA(i); /* previous call may change the stack */ + b = n; + L->top = ra + n; + } + for (j = 0; j < b; j++) { + if (j < n) { + setobjs2s(L, ra + j, ci->base - n + j); + } + else { + setnilvalue(ra + j); + } + } + continue; + } + } + } +} + diff --git a/lib/lua/lvm.h b/lib/lua/lvm.h new file mode 100644 index 000000000000..bfe4f5678d02 --- /dev/null +++ b/lib/lua/lvm.h @@ -0,0 +1,36 @@ +/* +** $Id: lvm.h,v 2.5.1.1 2007/12/27 13:02:25 roberto Exp $ +** Lua virtual machine +** See Copyright Notice in lua.h +*/ + +#ifndef lvm_h +#define lvm_h + + +#include "ldo.h" +#include "lobject.h" +#include "ltm.h" + + +#define tostring(L,o) ((ttype(o) == LUA_TSTRING) || (luaV_tostring(L, o))) + +#define tonumber(o,n) (ttype(o) == LUA_TNUMBER || \ + (((o) = luaV_tonumber(o,n)) != NULL)) + +#define equalobj(L,o1,o2) \ + (ttype(o1) == ttype(o2) && luaV_equalval(L, o1, o2)) + + +LUAI_FUNC int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r); +LUAI_FUNC int luaV_equalval (lua_State *L, const TValue *t1, const TValue *t2); +LUAI_FUNC const TValue *luaV_tonumber (const TValue *obj, TValue *n); +LUAI_FUNC int luaV_tostring (lua_State *L, StkId obj); +LUAI_FUNC void luaV_gettable (lua_State *L, const TValue *t, TValue *key, + StkId val); +LUAI_FUNC void luaV_settable (lua_State *L, const TValue *t, TValue *key, + StkId val); +LUAI_FUNC void luaV_execute (lua_State *L, int nexeccalls); +LUAI_FUNC void luaV_concat (lua_State *L, int total, int last); + +#endif diff --git a/lib/lua/lzio.c b/lib/lua/lzio.c new file mode 100644 index 000000000000..293edd59b08f --- /dev/null +++ b/lib/lua/lzio.c @@ -0,0 +1,82 @@ +/* +** $Id: lzio.c,v 1.31.1.1 2007/12/27 13:02:25 roberto Exp $ +** a generic input stream interface +** See Copyright Notice in lua.h +*/ + + +#include + +#define lzio_c +#define LUA_CORE + +#include "lua.h" + +#include "llimits.h" +#include "lmem.h" +#include "lstate.h" +#include "lzio.h" + + +int luaZ_fill (ZIO *z) { + size_t size; + lua_State *L = z->L; + const char *buff; + lua_unlock(L); + buff = z->reader(L, z->data, &size); + lua_lock(L); + if (buff == NULL || size == 0) return EOZ; + z->n = size - 1; + z->p = buff; + return char2int(*(z->p++)); +} + + +int luaZ_lookahead (ZIO *z) { + if (z->n == 0) { + if (luaZ_fill(z) == EOZ) + return EOZ; + else { + z->n++; /* luaZ_fill removed first byte; put back it */ + z->p--; + } + } + return char2int(*z->p); +} + + +void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) { + z->L = L; + z->reader = reader; + z->data = data; + z->n = 0; + z->p = NULL; +} + + +/* --------------------------------------------------------------- read --- */ +size_t luaZ_read (ZIO *z, void *b, size_t n) { + while (n) { + size_t m; + if (luaZ_lookahead(z) == EOZ) + return n; /* return number of missing bytes */ + m = (n <= z->n) ? n : z->n; /* min. between n and z->n */ + memcpy(b, z->p, m); + z->n -= m; + z->p += m; + b = (char *)b + m; + n -= m; + } + return 0; +} + +/* ------------------------------------------------------------------------ */ +char *luaZ_openspace (lua_State *L, Mbuffer *buff, size_t n) { + if (n > buff->buffsize) { + if (n < LUA_MINBUFFER) n = LUA_MINBUFFER; + luaZ_resizebuffer(L, buff, n); + } + return buff->buffer; +} + + diff --git a/lib/lua/lzio.h b/lib/lua/lzio.h new file mode 100644 index 000000000000..51d695d8c1d5 --- /dev/null +++ b/lib/lua/lzio.h @@ -0,0 +1,67 @@ +/* +** $Id: lzio.h,v 1.21.1.1 2007/12/27 13:02:25 roberto Exp $ +** Buffered streams +** See Copyright Notice in lua.h +*/ + + +#ifndef lzio_h +#define lzio_h + +#include "lua.h" + +#include "lmem.h" + + +#define EOZ (-1) /* end of stream */ + +typedef struct Zio ZIO; + +#define char2int(c) cast(int, cast(unsigned char, (c))) + +#define zgetc(z) (((z)->n--)>0 ? char2int(*(z)->p++) : luaZ_fill(z)) + +typedef struct Mbuffer { + char *buffer; + size_t n; + size_t buffsize; +} Mbuffer; + +#define luaZ_initbuffer(L, buff) ((buff)->buffer = NULL, (buff)->buffsize = 0) + +#define luaZ_buffer(buff) ((buff)->buffer) +#define luaZ_sizebuffer(buff) ((buff)->buffsize) +#define luaZ_bufflen(buff) ((buff)->n) + +#define luaZ_resetbuffer(buff) ((buff)->n = 0) + + +#define luaZ_resizebuffer(L, buff, size) \ + (luaM_reallocvector(L, (buff)->buffer, (buff)->buffsize, size, char), \ + (buff)->buffsize = size) + +#define luaZ_freebuffer(L, buff) luaZ_resizebuffer(L, buff, 0) + + +LUAI_FUNC char *luaZ_openspace (lua_State *L, Mbuffer *buff, size_t n); +LUAI_FUNC void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, + void *data); +LUAI_FUNC size_t luaZ_read (ZIO* z, void* b, size_t n); /* read next n bytes */ +LUAI_FUNC int luaZ_lookahead (ZIO *z); + + + +/* --------- Private Part ------------------ */ + +struct Zio { + size_t n; /* bytes still unread */ + const char *p; /* current position in buffer */ + lua_Reader reader; + void* data; /* additional data */ + lua_State *L; /* Lua state (for reader) */ +}; + + +LUAI_FUNC int luaZ_fill (ZIO *z); + +#endif diff --git a/lib/lua/print.c b/lib/lua/print.c new file mode 100644 index 000000000000..e240cfc3c608 --- /dev/null +++ b/lib/lua/print.c @@ -0,0 +1,227 @@ +/* +** $Id: print.c,v 1.55a 2006/05/31 13:30:05 lhf Exp $ +** print bytecodes +** See Copyright Notice in lua.h +*/ + +#include +#include + +#define luac_c +#define LUA_CORE + +#include "ldebug.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lundump.h" + +#define PrintFunction luaU_print + +#define Sizeof(x) ((int)sizeof(x)) +#define VOID(p) ((const void*)(p)) + +static void PrintString(const TString* ts) +{ + const char* s=getstr(ts); + size_t i,n=ts->tsv.len; + putchar('"'); + for (i=0; ik[i]; + switch (ttype(o)) + { + case LUA_TNIL: + printf("nil"); + break; + case LUA_TBOOLEAN: + printf(bvalue(o) ? "true" : "false"); + break; + case LUA_TNUMBER: + printf(LUA_NUMBER_FMT,nvalue(o)); + break; + case LUA_TSTRING: + PrintString(rawtsvalue(o)); + break; + default: /* cannot happen */ + printf("? type=%d",ttype(o)); + break; + } +} + +static void PrintCode(const Proto* f) +{ + const Instruction* code=f->code; + int pc,n=f->sizecode; + for (pc=0; pc0) printf("[%d]\t",line); else printf("[-]\t"); + printf("%-9s\t",luaP_opnames[o]); + switch (getOpMode(o)) + { + case iABC: + printf("%d",a); + if (getBMode(o)!=OpArgN) printf(" %d",ISK(b) ? (-1-INDEXK(b)) : b); + if (getCMode(o)!=OpArgN) printf(" %d",ISK(c) ? (-1-INDEXK(c)) : c); + break; + case iABx: + if (getBMode(o)==OpArgK) printf("%d %d",a,-1-bx); else printf("%d %d",a,bx); + break; + case iAsBx: + if (o==OP_JMP) printf("%d",sbx); else printf("%d %d",a,sbx); + break; + } + switch (o) + { + case OP_LOADK: + printf("\t; "); PrintConstant(f,bx); + break; + case OP_GETUPVAL: + case OP_SETUPVAL: + printf("\t; %s", (f->sizeupvalues>0) ? getstr(f->upvalues[b]) : "-"); + break; + case OP_GETGLOBAL: + case OP_SETGLOBAL: + printf("\t; %s",svalue(&f->k[bx])); + break; + case OP_GETTABLE: + case OP_SELF: + if (ISK(c)) { printf("\t; "); PrintConstant(f,INDEXK(c)); } + break; + case OP_SETTABLE: + case OP_ADD: + case OP_SUB: + case OP_MUL: + case OP_DIV: + case OP_POW: + case OP_EQ: + case OP_LT: + case OP_LE: + if (ISK(b) || ISK(c)) + { + printf("\t; "); + if (ISK(b)) PrintConstant(f,INDEXK(b)); else printf("-"); + printf(" "); + if (ISK(c)) PrintConstant(f,INDEXK(c)); else printf("-"); + } + break; + case OP_JMP: + case OP_FORLOOP: + case OP_FORPREP: + printf("\t; to %d",sbx+pc+2); + break; + case OP_CLOSURE: + printf("\t; %p",VOID(f->p[bx])); + break; + case OP_SETLIST: + if (c==0) printf("\t; %d",(int)code[++pc]); + else printf("\t; %d",c); + break; + default: + break; + } + printf("\n"); + } +} + +#define SS(x) (x==1)?"":"s" +#define S(x) x,SS(x) + +static void PrintHeader(const Proto* f) +{ + const char* s=getstr(f->source); + if (*s=='@' || *s=='=') + s++; + else if (*s==LUA_SIGNATURE[0]) + s="(bstring)"; + else + s="(string)"; + printf("\n%s <%s:%d,%d> (%d instruction%s, %d bytes at %p)\n", + (f->linedefined==0)?"main":"function",s, + f->linedefined,f->lastlinedefined, + S(f->sizecode),f->sizecode*Sizeof(Instruction),VOID(f)); + printf("%d%s param%s, %d slot%s, %d upvalue%s, ", + f->numparams,f->is_vararg?"+":"",SS(f->numparams), + S(f->maxstacksize),S(f->nups)); + printf("%d local%s, %d constant%s, %d function%s\n", + S(f->sizelocvars),S(f->sizek),S(f->sizep)); +} + +static void PrintConstants(const Proto* f) +{ + int i,n=f->sizek; + printf("constants (%d) for %p:\n",n,VOID(f)); + for (i=0; isizelocvars; + printf("locals (%d) for %p:\n",n,VOID(f)); + for (i=0; ilocvars[i].varname),f->locvars[i].startpc+1,f->locvars[i].endpc+1); + } +} + +static void PrintUpvalues(const Proto* f) +{ + int i,n=f->sizeupvalues; + printf("upvalues (%d) for %p:\n",n,VOID(f)); + if (f->upvalues==NULL) return; + for (i=0; iupvalues[i])); + } +} + +void PrintFunction(const Proto* f, int full) +{ + int i,n=f->sizep; + PrintHeader(f); + PrintCode(f); + if (full) + { + PrintConstants(f); + PrintLocals(f); + PrintUpvalues(f); + } + for (i=0; ip[i],full); +} From 89edf7cae1e37c7c95340f051db53add6658a1ae Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Sun, 9 Mar 2025 22:42:25 +0800 Subject: [PATCH 008/110] headers: move to common directory --- {lib/lua => include/linux}/lauxlib.h | 0 {lib/lua => include/linux}/lua.h | 0 {lib/lua => include/linux}/luaconf.h | 0 {lib/lua => include/linux}/lualib.h | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename {lib/lua => include/linux}/lauxlib.h (100%) rename {lib/lua => include/linux}/lua.h (100%) rename {lib/lua => include/linux}/luaconf.h (100%) rename {lib/lua => include/linux}/lualib.h (100%) diff --git a/lib/lua/lauxlib.h b/include/linux/lauxlib.h similarity index 100% rename from lib/lua/lauxlib.h rename to include/linux/lauxlib.h diff --git a/lib/lua/lua.h b/include/linux/lua.h similarity index 100% rename from lib/lua/lua.h rename to include/linux/lua.h diff --git a/lib/lua/luaconf.h b/include/linux/luaconf.h similarity index 100% rename from lib/lua/luaconf.h rename to include/linux/luaconf.h diff --git a/lib/lua/lualib.h b/include/linux/lualib.h similarity index 100% rename from lib/lua/lualib.h rename to include/linux/lualib.h From 590dd958daa22037614cf8724342266cf52015f7 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Fri, 22 Aug 2025 14:19:00 +0800 Subject: [PATCH 009/110] include: lua - fixes for kernel --- include/linux/lauxlib.h | 3 -- include/linux/lua.h | 5 +-- include/linux/luaconf.h | 67 ++++++++++++++++++++++++++++------------- include/linux/lualib.h | 2 +- 4 files changed, 50 insertions(+), 27 deletions(-) diff --git a/include/linux/lauxlib.h b/include/linux/lauxlib.h index 34258235dbeb..879ff2d2b60d 100644 --- a/include/linux/lauxlib.h +++ b/include/linux/lauxlib.h @@ -9,9 +9,6 @@ #define lauxlib_h -#include -#include - #include "lua.h" diff --git a/include/linux/lua.h b/include/linux/lua.h index a4b73e743ede..184b4b4560df 100644 --- a/include/linux/lua.h +++ b/include/linux/lua.h @@ -9,8 +9,9 @@ #ifndef lua_h #define lua_h -#include -#include +#include +#include +#include #include "luaconf.h" diff --git a/include/linux/luaconf.h b/include/linux/luaconf.h index e2cb26163a23..daeb1879e4c5 100644 --- a/include/linux/luaconf.h +++ b/include/linux/luaconf.h @@ -8,8 +8,9 @@ #ifndef lconfig_h #define lconfig_h -#include -#include +#include +#include +#include /* @@ -140,7 +141,11 @@ ** CHANGE that if ptrdiff_t is not adequate on your machine. (On most ** machines, ptrdiff_t gives a good choice between int or long.) */ +#ifdef __KERNEL__ +#define LUA_INTEGER LUA_NUMBER +#else #define LUA_INTEGER ptrdiff_t +#endif /* @@ -378,7 +383,7 @@ #include #define luai_apicheck(L,o) { (void)L; assert(o); } #else -#define luai_apicheck(L,o) { (void)L; } +#define luai_apicheck(L,o) { (void)L; WARN_ON(!(o)); } #endif @@ -387,15 +392,7 @@ ** CHANGE here if Lua cannot automatically detect the number of bits of ** your machine. Probably you do not need to change this. */ -/* avoid overflows in comparison */ -#if INT_MAX-20 < 32760 -#define LUAI_BITSINT 16 -#elif INT_MAX > 2147483640L -/* int has at least 32 bits */ #define LUAI_BITSINT 32 -#else -#error "you must define LUA_BITSINT with number of bits in an integer" -#endif /* @@ -485,7 +482,7 @@ /* @@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system. */ -#define LUAL_BUFFERSIZE BUFSIZ +#define LUAL_BUFFERSIZE 1024 /* }================================================================== */ @@ -501,15 +498,18 @@ ** =================================================================== */ +#ifdef __KERNEL__ +#define LUA_NUMBER long long +#else #define LUA_NUMBER_DOUBLE #define LUA_NUMBER double +#endif /* @@ LUAI_UACNUMBER is the result of an 'usual argument conversion' @* over a number. */ -#define LUAI_UACNUMBER double - +#define LUAI_UACNUMBER LUA_NUMBER /* @@ LUA_NUMBER_SCAN is the format for reading numbers. @@ -518,24 +518,38 @@ @@ LUAI_MAXNUMBER2STR is maximum size of previous conversion. @@ lua_str2number converts a string to a number. */ +#ifdef __KERNEL__ +#define LUA_NUMBER_SCAN "%lld" +#define LUA_NUMBER_FMT "%lld" +#define lua_str2number(s,p) simple_strtoul((s), (p), 10) +#else #define LUA_NUMBER_SCAN "%lf" #define LUA_NUMBER_FMT "%.14g" -#define lua_number2str(s,n) sprintf((s), LUA_NUMBER_FMT, (n)) -#define LUAI_MAXNUMBER2STR 32 /* 16 digits, sign, point, and \0 */ #define lua_str2number(s,p) strtod((s), (p)) +#endif +#define lua_number2str(s,l,n) snprintf((s), (l), LUA_NUMBER_FMT, (n)) +#define LUAI_MAXNUMBER2STR 32 /* 16 digits, sign, point, and \0 */ /* @@ The luai_num* macros define the primitive operations over numbers. */ #if defined(LUA_CORE) +#ifdef __KERNEL__ +/* XXX: we should raise an error instead of return INTMAX_MAX */ +#define luai_numdiv(a,b) ((b) != 0 ? (a)/(b) : INT_MAX) +#define luai_nummod(a,b) ((a)%(b)) +#define luai_numpow(a,b) luai_nummul(a,b) +#else #include -#define luai_numadd(a,b) ((a)+(b)) -#define luai_numsub(a,b) ((a)-(b)) -#define luai_nummul(a,b) ((a)*(b)) #define luai_numdiv(a,b) ((a)/(b)) #define luai_nummod(a,b) ((a) - floor((a)/(b))*(b)) #define luai_numpow(a,b) (pow(a,b)) +#endif + +#define luai_numadd(a,b) ((a)+(b)) +#define luai_numsub(a,b) ((a)-(b)) +#define luai_nummul(a,b) ((a)*(b)) #define luai_numunm(a) (-(a)) #define luai_numeq(a,b) ((a)==(b)) #define luai_numlt(a,b) ((a)<(b)) @@ -603,7 +617,12 @@ union luai_Cast { double l_d; long l_l; }; ** compiling as C++ code, with _longjmp/_setjmp when asked to use them, ** and with longjmp/setjmp otherwise. */ -#if defined(__cplusplus) +#ifdef __KERNEL__ +#define LUAI_THROW(L,c) longjmp(& ((c)->b), 1) +#define LUAI_TRY(L,c,a) if (setjmp(& ((c)->b)) == 0) { a } +#define luai_jmpbuf struct label_t + +#elif defined(__cplusplus) /* C++ exceptions */ #define LUAI_THROW(L,c) throw(c) #define LUAI_TRY(L,c,a) try { a } catch(...) \ @@ -736,7 +755,13 @@ union luai_Cast { double l_d; long l_l; }; ** CHANGE them if your system supports long long or does not support long. */ -#if defined(LUA_USELONGLONG) +#ifdef __KERNEL__ + +#define LUA_INTFRMLEN "ll" +#define LUA_INTFRM_T intptr_t +#define LUA_UINTFRM_T uintptr_t + +#elif defined(LUA_USELONGLONG) #define LUA_INTFRMLEN "ll" #define LUA_INTFRM_T long long diff --git a/include/linux/lualib.h b/include/linux/lualib.h index 469417f6703e..adec76a48ab1 100644 --- a/include/linux/lualib.h +++ b/include/linux/lualib.h @@ -46,7 +46,7 @@ LUALIB_API void (luaL_openlibs) (lua_State *L); #ifndef lua_assert -#define lua_assert(x) ((void)0) +#define lua_assert(x) WARN_ON(!(x)) #endif From 3f709af076db643bc3da408cc1a0ce86da6411e6 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Fri, 22 Aug 2025 14:18:30 +0800 Subject: [PATCH 010/110] lib: lua - fixes --- lib/lua/lapi.c | 8 +-- lib/lua/lauxlib.c | 18 +++--- lib/lua/lbaselib.c | 32 +++++++---- lib/lua/lcode.c | 4 +- lib/lua/ldblib.c | 16 ++++-- lib/lua/ldebug.c | 8 +-- lib/lua/ldo.c | 10 ++-- lib/lua/ldump.c | 6 +- lib/lua/lfunc.c | 4 +- lib/lua/lgc.c | 4 +- lib/lua/linit.c | 12 +++- lib/lua/liolib.c | 13 ++--- lib/lua/llex.c | 14 +++-- lib/lua/llimits.h | 6 +- lib/lua/lmathlib.c | 6 +- lib/lua/lmem.c | 4 +- lib/lua/lmem.h | 4 +- lib/lua/loadlib.c | 139 +++------------------------------------------ lib/lua/lobject.c | 15 +++-- lib/lua/lobject.h | 4 +- lib/lua/loslib.c | 6 +- lib/lua/lparser.c | 4 +- lib/lua/lstate.c | 4 +- lib/lua/lstate.h | 2 +- lib/lua/lstring.c | 4 +- lib/lua/lstrlib.c | 31 +++++----- lib/lua/ltable.c | 7 ++- lib/lua/ltablib.c | 10 ++-- lib/lua/ltm.c | 4 +- lib/lua/lundump.c | 4 +- lib/lua/lvm.c | 12 ++-- lib/lua/lzio.c | 4 +- lib/lua/lzio.h | 2 +- 33 files changed, 155 insertions(+), 266 deletions(-) diff --git a/lib/lua/lapi.c b/lib/lua/lapi.c index 5d5145d2ebad..6b741d2ed5e7 100644 --- a/lib/lua/lapi.c +++ b/lib/lua/lapi.c @@ -5,15 +5,13 @@ */ -#include -#include -#include -#include +#include +#include #define lapi_c #define LUA_CORE -#include "lua.h" +#include #include "lapi.h" #include "ldebug.h" diff --git a/lib/lua/lauxlib.c b/lib/lua/lauxlib.c index 10f14e2c088e..2b3b885fa080 100644 --- a/lib/lua/lauxlib.c +++ b/lib/lua/lauxlib.c @@ -5,12 +5,10 @@ */ -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include /* This file uses only the official API of Lua. @@ -20,9 +18,9 @@ #define lauxlib_c #define LUA_LIB -#include "lua.h" +#include -#include "lauxlib.h" +#include #define FREELIST_REF 0 /* free list of references */ @@ -519,6 +517,7 @@ LUALIB_API void luaL_unref (lua_State *L, int t, int ref) { ** ======================================================= */ +#ifndef __KERNEL__ typedef struct LoadF { int extraline; FILE *f; @@ -588,6 +587,7 @@ LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) { lua_remove(L, fnameindex); return status; } +#endif typedef struct LoadS { @@ -624,6 +624,7 @@ LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s) { /* }====================================================== */ +#ifndef __KERNEL__ static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) { (void)ud; (void)osize; @@ -649,4 +650,5 @@ LUALIB_API lua_State *luaL_newstate (void) { if (L) lua_atpanic(L, &panic); return L; } +#endif diff --git a/lib/lua/lbaselib.c b/lib/lua/lbaselib.c index 2ab550bd48de..81fd5eb5c859 100644 --- a/lib/lua/lbaselib.c +++ b/lib/lua/lbaselib.c @@ -5,19 +5,21 @@ */ +#define pr_fmt(fmt) "lua: " fmt -#include -#include -#include -#include +#include +#include +#include +#include +#include #define lbaselib_c #define LUA_LIB -#include "lua.h" +#include -#include "lauxlib.h" -#include "lualib.h" +#include +#include @@ -41,11 +43,11 @@ static int luaB_print (lua_State *L) { if (s == NULL) return luaL_error(L, LUA_QL("tostring") " must return a string to " LUA_QL("print")); - if (i>1) fputs("\t", stdout); - fputs(s, stdout); + if (i>1) pr_info("\t"); + pr_info("%s", s); lua_pop(L, 1); /* pop result */ } - fputs("\n", stdout); + pr_info("\n"); return 0; } @@ -64,7 +66,7 @@ static int luaB_tonumber (lua_State *L) { char *s2; unsigned long n; luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range"); - n = strtoul(s1, &s2, base); + n = simple_strtoul(s1, &s2, base); if (s1 != s2) { /* at least one valid digit? */ while (isspace((unsigned char)(*s2))) s2++; /* skip trailing spaces */ if (*s2 == '\0') { /* no invalid trailing characters? */ @@ -282,10 +284,12 @@ static int luaB_loadstring (lua_State *L) { } +#ifndef __KERNEL__ static int luaB_loadfile (lua_State *L) { const char *fname = luaL_optstring(L, 1, NULL); return load_aux(L, luaL_loadfile(L, fname)); } +#endif /* @@ -322,6 +326,7 @@ static int luaB_load (lua_State *L) { } +#ifndef __KERNEL__ static int luaB_dofile (lua_State *L) { const char *fname = luaL_optstring(L, 1, NULL); int n = lua_gettop(L); @@ -329,6 +334,7 @@ static int luaB_dofile (lua_State *L) { lua_call(L, 0, LUA_MULTRET); return lua_gettop(L) - n; } +#endif static int luaB_assert (lua_State *L) { @@ -447,12 +453,16 @@ static int luaB_newproxy (lua_State *L) { static const luaL_Reg base_funcs[] = { {"assert", luaB_assert}, {"collectgarbage", luaB_collectgarbage}, +#ifndef __KERNEL__ {"dofile", luaB_dofile}, +#endif {"error", luaB_error}, {"gcinfo", luaB_gcinfo}, {"getfenv", luaB_getfenv}, {"getmetatable", luaB_getmetatable}, +#ifndef __KERNEL__ {"loadfile", luaB_loadfile}, +#endif {"load", luaB_load}, {"loadstring", luaB_loadstring}, {"next", luaB_next}, diff --git a/lib/lua/lcode.c b/lib/lua/lcode.c index 679cb9cfd98b..0d6768caebff 100644 --- a/lib/lua/lcode.c +++ b/lib/lua/lcode.c @@ -5,12 +5,12 @@ */ -#include +#include #define lcode_c #define LUA_CORE -#include "lua.h" +#include #include "lcode.h" #include "ldebug.h" diff --git a/lib/lua/ldblib.c b/lib/lua/ldblib.c index 2027eda59837..1c3073f6a479 100644 --- a/lib/lua/ldblib.c +++ b/lib/lua/ldblib.c @@ -5,17 +5,17 @@ */ -#include -#include -#include +#include +#include +#include #define ldblib_c #define LUA_LIB -#include "lua.h" +#include -#include "lauxlib.h" -#include "lualib.h" +#include +#include @@ -299,6 +299,7 @@ static int db_gethook (lua_State *L) { } +#ifndef __KERNEL__ static int db_debug (lua_State *L) { for (;;) { char buffer[250]; @@ -314,6 +315,7 @@ static int db_debug (lua_State *L) { lua_settop(L, 0); /* remove eventual returns */ } } +#endif #define LEVELS1 12 /* size of the first part of the stack */ @@ -373,7 +375,9 @@ static int db_errorfb (lua_State *L) { static const luaL_Reg dblib[] = { +#ifndef __KERNEL__ {"debug", db_debug}, +#endif {"getfenv", db_getfenv}, {"gethook", db_gethook}, {"getinfo", db_getinfo}, diff --git a/lib/lua/ldebug.c b/lib/lua/ldebug.c index 50ad3d38035e..1f10f70e6e49 100644 --- a/lib/lua/ldebug.c +++ b/lib/lua/ldebug.c @@ -5,15 +5,14 @@ */ -#include -#include -#include +#include +#include #define ldebug_c #define LUA_CORE -#include "lua.h" +#include #include "lapi.h" #include "lcode.h" @@ -413,6 +412,7 @@ static Instruction symbexec (const Proto *pt, int lastpc, int reg) { case OP_FORPREP: checkreg(pt, a+3); /* go through */ + fallthrough; case OP_JMP: { int dest = pc+1+b; /* not full check and jump is forward and do not skip `lastpc'? */ diff --git a/lib/lua/ldo.c b/lib/lua/ldo.c index d1bf786cb720..d75dcb2c01c4 100644 --- a/lib/lua/ldo.c +++ b/lib/lua/ldo.c @@ -5,14 +5,14 @@ */ -#include -#include -#include +#include +#include +#include #define ldo_c #define LUA_CORE -#include "lua.h" +#include #include "ldebug.h" #include "ldo.h" @@ -103,7 +103,7 @@ void luaD_throw (lua_State *L, int errcode) { lua_unlock(L); G(L)->panic(L); } - exit(EXIT_FAILURE); + panic("Lua: unexpected error, errcode: %d\n", errcode); } } diff --git a/lib/lua/ldump.c b/lib/lua/ldump.c index c9d3d4870f4d..a3f741875f28 100644 --- a/lib/lua/ldump.c +++ b/lib/lua/ldump.c @@ -4,12 +4,10 @@ ** See Copyright Notice in lua.h */ -#include - #define ldump_c #define LUA_CORE -#include "lua.h" +#include #include "lobject.h" #include "lstate.h" @@ -60,7 +58,7 @@ static void DumpVector(const void* b, int n, size_t size, DumpState* D) static void DumpString(const TString* s, DumpState* D) { - if (s==NULL || getstr(s)==NULL) + if (s==NULL) { size_t size=0; DumpVar(size,D); diff --git a/lib/lua/lfunc.c b/lib/lua/lfunc.c index 813e88f5831b..c725279abcca 100644 --- a/lib/lua/lfunc.c +++ b/lib/lua/lfunc.c @@ -5,12 +5,10 @@ */ -#include - #define lfunc_c #define LUA_CORE -#include "lua.h" +#include #include "lfunc.h" #include "lgc.h" diff --git a/lib/lua/lgc.c b/lib/lua/lgc.c index e909c79a9696..71b241f157be 100644 --- a/lib/lua/lgc.c +++ b/lib/lua/lgc.c @@ -4,12 +4,12 @@ ** See Copyright Notice in lua.h */ -#include +#include #define lgc_c #define LUA_CORE -#include "lua.h" +#include #include "ldebug.h" #include "ldo.h" diff --git a/lib/lua/linit.c b/lib/lua/linit.c index c1f90dfab715..8c3d0793d5cb 100644 --- a/lib/lua/linit.c +++ b/lib/lua/linit.c @@ -8,20 +8,26 @@ #define linit_c #define LUA_LIB -#include "lua.h" +#include -#include "lualib.h" -#include "lauxlib.h" +#include +#include static const luaL_Reg lualibs[] = { {"", luaopen_base}, +#ifndef __KERNEL__ {LUA_LOADLIBNAME, luaopen_package}, +#endif {LUA_TABLIBNAME, luaopen_table}, +#ifndef __KERNEL__ {LUA_IOLIBNAME, luaopen_io}, {LUA_OSLIBNAME, luaopen_os}, +#endif {LUA_STRLIBNAME, luaopen_string}, +#ifndef __KERNEL__ {LUA_MATHLIBNAME, luaopen_math}, +#endif {LUA_DBLIBNAME, luaopen_debug}, {NULL, NULL} }; diff --git a/lib/lua/liolib.c b/lib/lua/liolib.c index 649f9a595155..a478243d5531 100644 --- a/lib/lua/liolib.c +++ b/lib/lua/liolib.c @@ -5,18 +5,17 @@ */ -#include -#include -#include -#include +#include +#include +#include #define liolib_c #define LUA_LIB -#include "lua.h" +#include -#include "lauxlib.h" -#include "lualib.h" +#include +#include diff --git a/lib/lua/llex.c b/lib/lua/llex.c index 88c6790c076a..7a66324edfbc 100644 --- a/lib/lua/llex.c +++ b/lib/lua/llex.c @@ -5,14 +5,13 @@ */ -#include -#include -#include +#include +#include #define llex_c #define LUA_CORE -#include "lua.h" +#include #include "ldo.h" #include "llex.h" @@ -23,6 +22,9 @@ #include "ltable.h" #include "lzio.h" +#ifndef UCHAR_MAX +#define UCHAR_MAX ((unsigned char)~0U) +#endif #define next(ls) (ls->current = zgetc(ls->z)) @@ -178,9 +180,8 @@ static void buffreplace (LexState *ls, char from, char to) { static void trydecpoint (LexState *ls, SemInfo *seminfo) { /* format error: try to update decimal point separator */ - struct lconv *cv = localeconv(); char old = ls->decpoint; - ls->decpoint = (cv ? cv->decimal_point[0] : '.'); + ls->decpoint = '.'; buffreplace(ls, old, ls->decpoint); /* try updated decimal separator */ if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r)) { /* format error with correct decimal point: no more options */ @@ -368,6 +369,7 @@ static int llex (LexState *ls, SemInfo *seminfo) { else if (sep == -1) return '['; else luaX_lexerror(ls, "invalid long string delimiter", TK_STRING); } + fallthrough; case '=': { next(ls); if (ls->current != '=') return '='; diff --git a/lib/lua/llimits.h b/lib/lua/llimits.h index ca8dcb72244b..01f63087c8fe 100644 --- a/lib/lua/llimits.h +++ b/lib/lua/llimits.h @@ -8,11 +8,11 @@ #define llimits_h -#include -#include +#include +#include -#include "lua.h" +#include typedef LUAI_UINT32 lu_int32; diff --git a/lib/lua/lmathlib.c b/lib/lua/lmathlib.c index 441fbf736c2b..7c79832c8124 100644 --- a/lib/lua/lmathlib.c +++ b/lib/lua/lmathlib.c @@ -11,10 +11,10 @@ #define lmathlib_c #define LUA_LIB -#include "lua.h" +#include -#include "lauxlib.h" -#include "lualib.h" +#include +#include #undef PI diff --git a/lib/lua/lmem.c b/lib/lua/lmem.c index ae7d8c965f6e..9bd12ad90257 100644 --- a/lib/lua/lmem.c +++ b/lib/lua/lmem.c @@ -5,12 +5,10 @@ */ -#include - #define lmem_c #define LUA_CORE -#include "lua.h" +#include #include "ldebug.h" #include "ldo.h" diff --git a/lib/lua/lmem.h b/lib/lua/lmem.h index 7c2dcb32207a..afac3fd9e6b1 100644 --- a/lib/lua/lmem.h +++ b/lib/lua/lmem.h @@ -8,10 +8,8 @@ #define lmem_h -#include - #include "llimits.h" -#include "lua.h" +#include #define MEMERRMSG "not enough memory" diff --git a/lib/lua/loadlib.c b/lib/lua/loadlib.c index 6158c5353df2..f92d28637190 100644 --- a/lib/lua/loadlib.c +++ b/lib/lua/loadlib.c @@ -9,17 +9,18 @@ */ -#include -#include +#include +#include +#include #define loadlib_c #define LUA_LIB -#include "lua.h" +#include -#include "lauxlib.h" -#include "lualib.h" +#include +#include /* prefix for open functions in C libraries */ @@ -329,109 +330,6 @@ static int ll_loadlib (lua_State *L) { */ -static int readable (const char *filename) { - FILE *f = fopen(filename, "r"); /* try to open file */ - if (f == NULL) return 0; /* open failed */ - fclose(f); - return 1; -} - - -static const char *pushnexttemplate (lua_State *L, const char *path) { - const char *l; - while (*path == *LUA_PATHSEP) path++; /* skip separators */ - if (*path == '\0') return NULL; /* no more templates */ - l = strchr(path, *LUA_PATHSEP); /* find next separator */ - if (l == NULL) l = path + strlen(path); - lua_pushlstring(L, path, l - path); /* template */ - return l; -} - - -static const char *findfile (lua_State *L, const char *name, - const char *pname) { - const char *path; - name = luaL_gsub(L, name, ".", LUA_DIRSEP); - lua_getfield(L, LUA_ENVIRONINDEX, pname); - path = lua_tostring(L, -1); - if (path == NULL) - luaL_error(L, LUA_QL("package.%s") " must be a string", pname); - lua_pushliteral(L, ""); /* error accumulator */ - while ((path = pushnexttemplate(L, path)) != NULL) { - const char *filename; - filename = luaL_gsub(L, lua_tostring(L, -1), LUA_PATH_MARK, name); - lua_remove(L, -2); /* remove path template */ - if (readable(filename)) /* does file exist and is readable? */ - return filename; /* return that file name */ - lua_pushfstring(L, "\n\tno file " LUA_QS, filename); - lua_remove(L, -2); /* remove file name */ - lua_concat(L, 2); /* add entry to possible error message */ - } - return NULL; /* not found */ -} - - -static void loaderror (lua_State *L, const char *filename) { - luaL_error(L, "error loading module " LUA_QS " from file " LUA_QS ":\n\t%s", - lua_tostring(L, 1), filename, lua_tostring(L, -1)); -} - - -static int loader_Lua (lua_State *L) { - const char *filename; - const char *name = luaL_checkstring(L, 1); - filename = findfile(L, name, "path"); - if (filename == NULL) return 1; /* library not found in this path */ - if (luaL_loadfile(L, filename) != 0) - loaderror(L, filename); - return 1; /* library loaded successfully */ -} - - -static const char *mkfuncname (lua_State *L, const char *modname) { - const char *funcname; - const char *mark = strchr(modname, *LUA_IGMARK); - if (mark) modname = mark + 1; - funcname = luaL_gsub(L, modname, ".", LUA_OFSEP); - funcname = lua_pushfstring(L, POF"%s", funcname); - lua_remove(L, -2); /* remove 'gsub' result */ - return funcname; -} - - -static int loader_C (lua_State *L) { - const char *funcname; - const char *name = luaL_checkstring(L, 1); - const char *filename = findfile(L, name, "cpath"); - if (filename == NULL) return 1; /* library not found in this path */ - funcname = mkfuncname(L, name); - if (ll_loadfunc(L, filename, funcname) != 0) - loaderror(L, filename); - return 1; /* library loaded successfully */ -} - - -static int loader_Croot (lua_State *L) { - const char *funcname; - const char *filename; - const char *name = luaL_checkstring(L, 1); - const char *p = strchr(name, '.'); - int stat; - if (p == NULL) return 0; /* is root */ - lua_pushlstring(L, name, p - name); - filename = findfile(L, lua_tostring(L, -1), "cpath"); - if (filename == NULL) return 1; /* root not found */ - funcname = mkfuncname(L, name); - if ((stat = ll_loadfunc(L, filename, funcname)) != 0) { - if (stat != ERRFUNC) loaderror(L, filename); /* real error */ - lua_pushfstring(L, "\n\tno module " LUA_QS " in file " LUA_QS, - name, filename); - return 1; /* function not found */ - } - return 1; -} - - static int loader_preload (lua_State *L) { const char *name = luaL_checkstring(L, 1); lua_getfield(L, LUA_ENVIRONINDEX, "preload"); @@ -585,27 +483,6 @@ static int ll_seeall (lua_State *L) { /* }====================================================== */ - -/* auxiliary mark (for internal use) */ -#define AUXMARK "\1" - -static void setpath (lua_State *L, const char *fieldname, const char *envname, - const char *def) { - const char *path = getenv(envname); - if (path == NULL) /* no environment variable? */ - lua_pushstring(L, def); /* use default */ - else { - /* replace ";;" by ";AUXMARK;" and then AUXMARK by default path */ - path = luaL_gsub(L, path, LUA_PATHSEP LUA_PATHSEP, - LUA_PATHSEP AUXMARK LUA_PATHSEP); - luaL_gsub(L, path, AUXMARK, def); - lua_remove(L, -2); - } - setprogdir(L); - lua_setfield(L, -2, fieldname); -} - - static const luaL_Reg pk_funcs[] = { {"loadlib", ll_loadlib}, {"seeall", ll_seeall}, @@ -621,7 +498,7 @@ static const luaL_Reg ll_funcs[] = { static const lua_CFunction loaders[] = - {loader_preload, loader_Lua, loader_C, loader_Croot, NULL}; + {loader_preload, NULL}; LUALIB_API int luaopen_package (lua_State *L) { @@ -646,8 +523,6 @@ LUALIB_API int luaopen_package (lua_State *L) { lua_rawseti(L, -2, i+1); } lua_setfield(L, -2, "loaders"); /* put it in field `loaders' */ - setpath(L, "path", LUA_PATH, LUA_PATH_DEFAULT); /* set field `path' */ - setpath(L, "cpath", LUA_CPATH, LUA_CPATH_DEFAULT); /* set field `cpath' */ /* store config information */ lua_pushliteral(L, LUA_DIRSEP "\n" LUA_PATHSEP "\n" LUA_PATH_MARK "\n" LUA_EXECDIR "\n" LUA_IGMARK); diff --git a/lib/lua/lobject.c b/lib/lua/lobject.c index 4ff50732a4a9..183e39676dd0 100644 --- a/lib/lua/lobject.c +++ b/lib/lua/lobject.c @@ -4,16 +4,15 @@ ** See Copyright Notice in lua.h */ -#include -#include -#include -#include -#include +#include +#include +#include +#include #define lobject_c #define LUA_CORE -#include "lua.h" +#include #include "ldo.h" #include "lmem.h" @@ -92,7 +91,7 @@ int luaO_str2d (const char *s, lua_Number *result) { *result = lua_str2number(s, &endptr); if (endptr == s) return 0; /* conversion failed */ if (*endptr == 'x' || *endptr == 'X') /* maybe an hexadecimal constant? */ - *result = cast_num(strtoul(s, &endptr, 16)); + *result = cast_num(simple_strtoul(s, &endptr, 16)); if (*endptr == '\0') return 1; /* most common case */ while (isspace(cast(unsigned char, *endptr))) endptr++; if (*endptr != '\0') return 0; /* invalid trailing characters? */ @@ -142,7 +141,7 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { } case 'p': { char buff[4*sizeof(void *) + 8]; /* should be enough space for a `%p' */ - sprintf(buff, "%p", va_arg(argp, void *)); + snprintf(buff, sizeof(buff), "%p", va_arg(argp, void *)); pushstr(L, buff); break; } diff --git a/lib/lua/lobject.h b/lib/lua/lobject.h index f1e447ef3ba8..b6bb2929ab35 100644 --- a/lib/lua/lobject.h +++ b/lib/lua/lobject.h @@ -9,11 +9,11 @@ #define lobject_h -#include +#include #include "llimits.h" -#include "lua.h" +#include /* tags for values visible from Lua */ diff --git a/lib/lua/loslib.c b/lib/lua/loslib.c index da06a572acf6..920ff6bfdb82 100644 --- a/lib/lua/loslib.c +++ b/lib/lua/loslib.c @@ -14,10 +14,10 @@ #define loslib_c #define LUA_LIB -#include "lua.h" +#include -#include "lauxlib.h" -#include "lualib.h" +#include +#include static int os_pushresult (lua_State *L, int i, const char *filename) { diff --git a/lib/lua/lparser.c b/lib/lua/lparser.c index dda7488dcad9..cc408c0a266c 100644 --- a/lib/lua/lparser.c +++ b/lib/lua/lparser.c @@ -5,12 +5,12 @@ */ -#include +#include #define lparser_c #define LUA_CORE -#include "lua.h" +#include #include "lcode.h" #include "ldebug.h" diff --git a/lib/lua/lstate.c b/lib/lua/lstate.c index 4313b83a0c7c..75f58a2da128 100644 --- a/lib/lua/lstate.c +++ b/lib/lua/lstate.c @@ -5,12 +5,10 @@ */ -#include - #define lstate_c #define LUA_CORE -#include "lua.h" +#include #include "ldebug.h" #include "ldo.h" diff --git a/lib/lua/lstate.h b/lib/lua/lstate.h index 3bc575b6bc8c..898d12631709 100644 --- a/lib/lua/lstate.h +++ b/lib/lua/lstate.h @@ -7,7 +7,7 @@ #ifndef lstate_h #define lstate_h -#include "lua.h" +#include #include "lobject.h" #include "ltm.h" diff --git a/lib/lua/lstring.c b/lib/lua/lstring.c index 49113151cc70..ed61f8ca2c47 100644 --- a/lib/lua/lstring.c +++ b/lib/lua/lstring.c @@ -5,12 +5,12 @@ */ -#include +#include #define lstring_c #define LUA_CORE -#include "lua.h" +#include #include "lmem.h" #include "lobject.h" diff --git a/lib/lua/lstrlib.c b/lib/lua/lstrlib.c index 7a03489bebd4..f9672c80658b 100644 --- a/lib/lua/lstrlib.c +++ b/lib/lua/lstrlib.c @@ -5,19 +5,18 @@ */ -#include -#include -#include -#include -#include +#include +#include +#include +#include #define lstrlib_c #define LUA_LIB -#include "lua.h" +#include -#include "lauxlib.h" -#include "lualib.h" +#include +#include /* macro to `unsign' a character */ @@ -774,24 +773,30 @@ static int str_format (lua_State *L) { strfrmt = scanformat(L, strfrmt, form); switch (*strfrmt++) { case 'c': { - sprintf(buff, form, (int)luaL_checknumber(L, arg)); + snprintf(buff, sizeof(buff), form, (int)luaL_checknumber(L, arg)); break; } case 'd': case 'i': { addintlen(form); - sprintf(buff, form, (LUA_INTFRM_T)luaL_checknumber(L, arg)); + snprintf(buff, sizeof(buff), form, (LUA_INTFRM_T)luaL_checknumber(L, arg)); break; } case 'o': case 'u': case 'x': case 'X': { addintlen(form); - sprintf(buff, form, (unsigned LUA_INTFRM_T)luaL_checknumber(L, arg)); +#ifndef __KERNEL__ + snprintf(buff, sizeof(buff), form, (unsigned LUA_INTFRM_T)luaL_checknumber(L, arg)); +#else + snprintf(buff, sizeof(buff), form, (LUA_UINTFRM_T)luaL_checknumber(L, arg)); +#endif break; } +#ifndef __KERNEL__ case 'e': case 'E': case 'f': case 'g': case 'G': { - sprintf(buff, form, (double)luaL_checknumber(L, arg)); + snprintf(buff, sizeof(buff), form, (double)luaL_checknumber(L, arg)); break; } +#endif case 'q': { addquoted(L, &b, arg); continue; /* skip the 'addsize' at the end */ @@ -807,7 +812,7 @@ static int str_format (lua_State *L) { continue; /* skip the `addsize' at the end */ } else { - sprintf(buff, form, s); + snprintf(buff, sizeof(buff), form, s); break; } } diff --git a/lib/lua/ltable.c b/lib/lua/ltable.c index ec84f4fabc51..e1b0297ec76f 100644 --- a/lib/lua/ltable.c +++ b/lib/lua/ltable.c @@ -18,13 +18,13 @@ ** Hence even when the load factor reaches 100%, performance remains good. */ -#include -#include +#include +#include #define ltable_c #define LUA_CORE -#include "lua.h" +#include #include "ldebug.h" #include "ldo.h" @@ -478,6 +478,7 @@ const TValue *luaH_get (Table *t, const TValue *key) { return luaH_getnum(t, k); /* use specialized version */ /* else go through */ } + fallthrough; default: { Node *n = mainposition(t, key); do { /* check whether `key' is somewhere in the chain */ diff --git a/lib/lua/ltablib.c b/lib/lua/ltablib.c index b6d9cb4ac74d..7d6a87962754 100644 --- a/lib/lua/ltablib.c +++ b/lib/lua/ltablib.c @@ -5,15 +5,13 @@ */ -#include - #define ltablib_c #define LUA_LIB -#include "lua.h" +#include -#include "lauxlib.h" -#include "lualib.h" +#include +#include #define aux_getn(L,n) (luaL_checktype(L, n, LUA_TTABLE), luaL_getn(L, n)) @@ -137,7 +135,7 @@ static void addfield (lua_State *L, luaL_Buffer *b, int i) { if (!lua_isstring(L, -1)) luaL_error(L, "invalid value (%s) at index %d in table for " LUA_QL("concat"), luaL_typename(L, -1), i); - luaL_addvalue(b); + luaL_addvalue(b); } diff --git a/lib/lua/ltm.c b/lib/lua/ltm.c index c27f0f6fab85..241493937bde 100644 --- a/lib/lua/ltm.c +++ b/lib/lua/ltm.c @@ -5,12 +5,12 @@ */ -#include +#include #define ltm_c #define LUA_CORE -#include "lua.h" +#include #include "lobject.h" #include "lstate.h" diff --git a/lib/lua/lundump.c b/lib/lua/lundump.c index 8010a45795ba..16d1c258d27b 100644 --- a/lib/lua/lundump.c +++ b/lib/lua/lundump.c @@ -4,12 +4,12 @@ ** See Copyright Notice in lua.h */ -#include +#include #define lundump_c #define LUA_CORE -#include "lua.h" +#include #include "ldebug.h" #include "ldo.h" diff --git a/lib/lua/lvm.c b/lib/lua/lvm.c index e0a0cd85219d..31855c42085f 100644 --- a/lib/lua/lvm.c +++ b/lib/lua/lvm.c @@ -5,14 +5,14 @@ */ -#include -#include -#include +#include +#include +#include #define lvm_c #define LUA_CORE -#include "lua.h" +#include #include "ldebug.h" #include "ldo.h" @@ -50,7 +50,7 @@ int luaV_tostring (lua_State *L, StkId obj) { else { char s[LUAI_MAXNUMBER2STR]; lua_Number n = nvalue(obj); - lua_number2str(s, n); + lua_number2str(s, sizeof(s), n); setsvalue2s(L, obj, luaS_new(L, s)); return 1; } @@ -206,7 +206,7 @@ static int l_strcmp (const TString *ls, const TString *rs) { const char *r = getstr(rs); size_t lr = rs->tsv.len; for (;;) { - int temp = strcoll(l, r); + int temp = strcmp(l, r); if (temp != 0) return temp; else { /* strings are equal up to a `\0' */ size_t len = strlen(l); /* index of first `\0' in both strings */ diff --git a/lib/lua/lzio.c b/lib/lua/lzio.c index 293edd59b08f..f2411cf1f169 100644 --- a/lib/lua/lzio.c +++ b/lib/lua/lzio.c @@ -5,12 +5,12 @@ */ -#include +#include #define lzio_c #define LUA_CORE -#include "lua.h" +#include #include "llimits.h" #include "lmem.h" diff --git a/lib/lua/lzio.h b/lib/lua/lzio.h index 51d695d8c1d5..a232f156d7b5 100644 --- a/lib/lua/lzio.h +++ b/lib/lua/lzio.h @@ -8,7 +8,7 @@ #ifndef lzio_h #define lzio_h -#include "lua.h" +#include #include "lmem.h" From 6f2930d2fcac92beb6fb855c9a840e149ca53834 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Fri, 22 Aug 2025 14:23:55 +0800 Subject: [PATCH 011/110] lib: lua - add makefile --- lib/lua/Makefile | 218 ++++++++--------------------------------------- 1 file changed, 36 insertions(+), 182 deletions(-) diff --git a/lib/lua/Makefile b/lib/lua/Makefile index e0d4c9fa649c..ee7a2e28575c 100644 --- a/lib/lua/Makefile +++ b/lib/lua/Makefile @@ -1,182 +1,36 @@ -# makefile for building Lua -# see ../INSTALL for installation instructions -# see ../Makefile and luaconf.h for further customization - -# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT ======================= - -# Your platform. See PLATS for possible values. -PLAT= none - -CC= gcc -CFLAGS= -O2 -Wall $(MYCFLAGS) -AR= ar rcu -RANLIB= ranlib -RM= rm -f -LIBS= -lm $(MYLIBS) - -MYCFLAGS= -MYLDFLAGS= -MYLIBS= - -# == END OF USER SETTINGS. NO NEED TO CHANGE ANYTHING BELOW THIS LINE ========= - -PLATS= aix ansi bsd freebsd generic linux macosx mingw posix solaris - -LUA_A= liblua.a -CORE_O= lapi.o lcode.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o \ - lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o \ - lundump.o lvm.o lzio.o -LIB_O= lauxlib.o lbaselib.o ldblib.o liolib.o lmathlib.o loslib.o ltablib.o \ - lstrlib.o loadlib.o linit.o - -LUA_T= lua -LUA_O= lua.o - -LUAC_T= luac -LUAC_O= luac.o print.o - -ALL_O= $(CORE_O) $(LIB_O) $(LUA_O) $(LUAC_O) -ALL_T= $(LUA_A) $(LUA_T) $(LUAC_T) -ALL_A= $(LUA_A) - -default: $(PLAT) - -all: $(ALL_T) - -o: $(ALL_O) - -a: $(ALL_A) - -$(LUA_A): $(CORE_O) $(LIB_O) - $(AR) $@ $(CORE_O) $(LIB_O) # DLL needs all object files - $(RANLIB) $@ - -$(LUA_T): $(LUA_O) $(LUA_A) - $(CC) -o $@ $(MYLDFLAGS) $(LUA_O) $(LUA_A) $(LIBS) - -$(LUAC_T): $(LUAC_O) $(LUA_A) - $(CC) -o $@ $(MYLDFLAGS) $(LUAC_O) $(LUA_A) $(LIBS) - -clean: - $(RM) $(ALL_T) $(ALL_O) - -depend: - @$(CC) $(CFLAGS) -MM l*.c print.c - -echo: - @echo "PLAT = $(PLAT)" - @echo "CC = $(CC)" - @echo "CFLAGS = $(CFLAGS)" - @echo "AR = $(AR)" - @echo "RANLIB = $(RANLIB)" - @echo "RM = $(RM)" - @echo "MYCFLAGS = $(MYCFLAGS)" - @echo "MYLDFLAGS = $(MYLDFLAGS)" - @echo "MYLIBS = $(MYLIBS)" - -# convenience targets for popular platforms - -none: - @echo "Please choose a platform:" - @echo " $(PLATS)" - -aix: - $(MAKE) all CC="xlc" CFLAGS="-O2 -DLUA_USE_POSIX -DLUA_USE_DLOPEN" MYLIBS="-ldl" MYLDFLAGS="-brtl -bexpall" - -ansi: - $(MAKE) all MYCFLAGS=-DLUA_ANSI - -bsd: - $(MAKE) all MYCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN" MYLIBS="-Wl,-E" - -freebsd: - $(MAKE) all MYCFLAGS="-DLUA_USE_LINUX" MYLIBS="-Wl,-E -lreadline" - -generic: - $(MAKE) all MYCFLAGS= - -linux: - $(MAKE) all MYCFLAGS=-DLUA_USE_LINUX MYLIBS="-Wl,-E -ldl -lreadline -lhistory -lncurses" - -macosx: - $(MAKE) all MYCFLAGS=-DLUA_USE_LINUX MYLIBS="-lreadline" -# use this on Mac OS X 10.3- -# $(MAKE) all MYCFLAGS=-DLUA_USE_MACOSX - -mingw: - $(MAKE) "LUA_A=lua51.dll" "LUA_T=lua.exe" \ - "AR=$(CC) -shared -o" "RANLIB=strip --strip-unneeded" \ - "MYCFLAGS=-DLUA_BUILD_AS_DLL" "MYLIBS=" "MYLDFLAGS=-s" lua.exe - $(MAKE) "LUAC_T=luac.exe" luac.exe - -posix: - $(MAKE) all MYCFLAGS=-DLUA_USE_POSIX - -solaris: - $(MAKE) all MYCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN" MYLIBS="-ldl" - -# list targets that do not create files (but not all makes understand .PHONY) -.PHONY: all $(PLATS) default o a clean depend echo none - -# DO NOT DELETE - -lapi.o: lapi.c lua.h luaconf.h lapi.h lobject.h llimits.h ldebug.h \ - lstate.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h \ - lundump.h lvm.h -lauxlib.o: lauxlib.c lua.h luaconf.h lauxlib.h -lbaselib.o: lbaselib.c lua.h luaconf.h lauxlib.h lualib.h -lcode.o: lcode.c lua.h luaconf.h lcode.h llex.h lobject.h llimits.h \ - lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h ldo.h lgc.h \ - ltable.h -ldblib.o: ldblib.c lua.h luaconf.h lauxlib.h lualib.h -ldebug.o: ldebug.c lua.h luaconf.h lapi.h lobject.h llimits.h lcode.h \ - llex.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h ldo.h \ - lfunc.h lstring.h lgc.h ltable.h lvm.h -ldo.o: ldo.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \ - lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lparser.h lstring.h \ - ltable.h lundump.h lvm.h -ldump.o: ldump.c lua.h luaconf.h lobject.h llimits.h lstate.h ltm.h \ - lzio.h lmem.h lundump.h -lfunc.o: lfunc.c lua.h luaconf.h lfunc.h lobject.h llimits.h lgc.h lmem.h \ - lstate.h ltm.h lzio.h -lgc.o: lgc.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \ - lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h -linit.o: linit.c lua.h luaconf.h lualib.h lauxlib.h -liolib.o: liolib.c lua.h luaconf.h lauxlib.h lualib.h -llex.o: llex.c lua.h luaconf.h ldo.h lobject.h llimits.h lstate.h ltm.h \ - lzio.h lmem.h llex.h lparser.h lstring.h lgc.h ltable.h -lmathlib.o: lmathlib.c lua.h luaconf.h lauxlib.h lualib.h -lmem.o: lmem.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \ - ltm.h lzio.h lmem.h ldo.h -loadlib.o: loadlib.c lua.h luaconf.h lauxlib.h lualib.h -lobject.o: lobject.c lua.h luaconf.h ldo.h lobject.h llimits.h lstate.h \ - ltm.h lzio.h lmem.h lstring.h lgc.h lvm.h -lopcodes.o: lopcodes.c lopcodes.h llimits.h lua.h luaconf.h -loslib.o: loslib.c lua.h luaconf.h lauxlib.h lualib.h -lparser.o: lparser.c lua.h luaconf.h lcode.h llex.h lobject.h llimits.h \ - lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h ldo.h \ - lfunc.h lstring.h lgc.h ltable.h -lstate.o: lstate.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \ - ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h llex.h lstring.h ltable.h -lstring.o: lstring.c lua.h luaconf.h lmem.h llimits.h lobject.h lstate.h \ - ltm.h lzio.h lstring.h lgc.h -lstrlib.o: lstrlib.c lua.h luaconf.h lauxlib.h lualib.h -ltable.o: ltable.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \ - ltm.h lzio.h lmem.h ldo.h lgc.h ltable.h -ltablib.o: ltablib.c lua.h luaconf.h lauxlib.h lualib.h -ltm.o: ltm.c lua.h luaconf.h lobject.h llimits.h lstate.h ltm.h lzio.h \ - lmem.h lstring.h lgc.h ltable.h -lua.o: lua.c lua.h luaconf.h lauxlib.h lualib.h -luac.o: luac.c lua.h luaconf.h lauxlib.h ldo.h lobject.h llimits.h \ - lstate.h ltm.h lzio.h lmem.h lfunc.h lopcodes.h lstring.h lgc.h \ - lundump.h -lundump.o: lundump.c lua.h luaconf.h ldebug.h lstate.h lobject.h \ - llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lstring.h lgc.h lundump.h -lvm.o: lvm.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \ - lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lstring.h ltable.h lvm.h -lzio.o: lzio.c lua.h luaconf.h llimits.h lmem.h lstate.h lobject.h ltm.h \ - lzio.h -print.o: print.c ldebug.h lstate.h lua.h luaconf.h lobject.h llimits.h \ - ltm.h lzio.h lmem.h lopcodes.h lundump.h - -# (end of Makefile) +# SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause + +obj-$(CONFIG_LUA) += lua-core.o lua-lib.o + +lua-core-y := \ + lapi.o \ + lcode.o \ + ldebug.o \ + ldo.o \ + ldump.o \ + lfunc.o \ + lgc.o \ + llex.o \ + lmem.o \ + lobject.o \ + lopcodes.o \ + lparser.o \ + lstate.o \ + lstring.o \ + ltable.o \ + ltm.o \ + lundump.o \ + lvm.o \ + lzio.o + +lua-lib-y := \ + lauxlib.o \ + lbaselib.o \ + ldblib.o \ + liolib.o \ + lmathlib.o \ + loslib.o \ + ltablib.o \ + lstrlib.o \ + loadlib.o \ + linit.o From 7b6f290886b5c8de8318289959a53c30fc4f0afb Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Fri, 22 Aug 2025 14:13:56 +0800 Subject: [PATCH 012/110] lib: lua - remove unused files from Makefile --- lib/lua/Makefile | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/lua/Makefile b/lib/lua/Makefile index ee7a2e28575c..b6bbafffb8cb 100644 --- a/lib/lua/Makefile +++ b/lib/lua/Makefile @@ -27,9 +27,6 @@ lua-lib-y := \ lauxlib.o \ lbaselib.o \ ldblib.o \ - liolib.o \ - lmathlib.o \ - loslib.o \ ltablib.o \ lstrlib.o \ loadlib.o \ From 0ad2265b1e937875046916afc6335986989bff9f Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Fri, 22 Aug 2025 14:23:23 +0800 Subject: [PATCH 013/110] lib: build lua library --- lib/Kconfig | 12 ++++++++++++ lib/Makefile | 2 ++ 2 files changed, 14 insertions(+) diff --git a/lib/Kconfig b/lib/Kconfig index 2923924bea78..af647befcbb2 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -649,3 +649,15 @@ config UNION_FIND config MIN_HEAP bool + +config ARCH_HAS_SETJMP + bool + +config SETJMP_ARCH + tristate + depends on ARCH_HAS_SETJMP + +config LUA + tristate + default n + select SETJMP_ARCH diff --git a/lib/Makefile b/lib/Makefile index aaf677cf4527..be50b6e8524b 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -246,6 +246,8 @@ obj-$(CONFIG_IRQ_POLL) += irq_poll.o obj-$(CONFIG_POLYNOMIAL) += polynomial.o +obj-$(CONFIG_LUA) += lua/ + # stackdepot.c should not be instrumented or call instrumented functions. # Prevent the compiler from calling builtins like memcmp() or bcmp() from this # file. From 0704b3c5bcc7be5d15ba94cf5001446aa17f1258 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Thu, 27 Mar 2025 15:40:58 +0800 Subject: [PATCH 014/110] lua-lsm: lib - add queue/tree/bitmap header queue.h and tree.h are borrowed from freebsd d342ae67192f ("uath: add support for GCMP-128 encryption") bitmap.h is borrowed from bitops of latest netbsd. Signed-off-by: Tianjia Zhang --- security/lua/bitmap.h | 63 +++ security/lua/queue.h | 970 +++++++++++++++++++++++++++++++++++++ security/lua/tree.h | 1062 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 2095 insertions(+) create mode 100644 security/lua/bitmap.h create mode 100644 security/lua/queue.h create mode 100644 security/lua/tree.h diff --git a/security/lua/bitmap.h b/security/lua/bitmap.h new file mode 100644 index 000000000000..54d2b4fa1757 --- /dev/null +++ b/security/lua/bitmap.h @@ -0,0 +1,63 @@ +/* $NetBSD: bitops.h,v 1.16 2024/05/12 10:34:56 rillig Exp $ */ + +/*- + * Copyright (c) 2007, 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas and Joerg Sonnenberger. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ +#ifndef _SYS_BITMAP_H_ +#define _SYS_BITMAP_H_ + +#include + +#define __BITMAP_TYPE(__s, __t, __n) struct __s { \ + __t _b[__BITMAP_SIZE(__t, __n)]; \ +} + +#define __BITMAP_BITS(__t) (sizeof(__t) * 8) +#define __BITMAP_SHIFT(__t) (ilog2(__BITMAP_BITS(__t))) +#define __BITMAP_MASK(__t) (__BITMAP_BITS(__t) - 1) +#define __BITMAP_SIZE(__t, __n) \ + (((__n) + (__BITMAP_BITS(__t) - 1)) / __BITMAP_BITS(__t)) +#define __BITMAP_BIT(__n, __v) \ + ((__typeof__((__v)->_b[0]))1 << ((__n) & __BITMAP_MASK(*(__v)->_b))) +#define __BITMAP_WORD(__n, __v) \ + ((__n) >> __BITMAP_SHIFT(*(__v)->_b)) + +#define __BITMAP_SET(__n, __v) \ + ((__v)->_b[__BITMAP_WORD(__n, __v)] |= __BITMAP_BIT(__n, __v)) +#define __BITMAP_CLR(__n, __v) \ + ((__v)->_b[__BITMAP_WORD(__n, __v)] &= ~__BITMAP_BIT(__n, __v)) +#define __BITMAP_ISSET(__n, __v) \ + ((__v)->_b[__BITMAP_WORD(__n, __v)] & __BITMAP_BIT(__n, __v)) + +#define __BITMAP_ZERO(__v) do { \ + size_t __i; \ + for (__i = 0; __i < ARRAY_SIZE((__v)->_b); __i++) \ + (__v)->_b[__i] = 0; \ + } while (0) + +#endif /* _SYS_BITMAP_H_ */ diff --git a/security/lua/queue.h b/security/lua/queue.h new file mode 100644 index 000000000000..70d13ee92617 --- /dev/null +++ b/security/lua/queue.h @@ -0,0 +1,970 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +#include + +/* + * This file defines four types of data structures: singly-linked lists, + * singly-linked tail queues, lists and tail queues. + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A singly-linked tail queue is headed by a pair of pointers, one to the + * head of the list and the other to the tail of the list. The elements are + * singly linked for minimum space and pointer manipulation overhead at the + * expense of O(n) removal for arbitrary elements. New elements can be added + * to the list after an existing element, at the head of the list, or at the + * end of the list. Elements being removed from the head of the tail queue + * should use the explicit macro for this purpose for optimum efficiency. + * A singly-linked tail queue may only be traversed in the forward direction. + * Singly-linked tail queues are ideal for applications with large datasets + * and few or no removals or for implementing a FIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may be traversed in either direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * For details on the use of these macros, see the queue(3) manual page. + * + * Below is a summary of implemented functions where: + * + means the macro is available + * - means the macro is not available + * s means the macro is available but is slow (runs in O(n) time) + * + * SLIST LIST STAILQ TAILQ + * _HEAD + + + + + * _CLASS_HEAD + + + + + * _HEAD_INITIALIZER + + + + + * _ENTRY + + + + + * _CLASS_ENTRY + + + + + * _INIT + + + + + * _EMPTY + + + + + * _END + + + + + * _FIRST + + + + + * _NEXT + + + + + * _PREV - + - + + * _LAST - - + + + * _LAST_FAST - - - + + * _FOREACH + + + + + * _FOREACH_FROM + + + + + * _FOREACH_SAFE + + + + + * _FOREACH_FROM_SAFE + + + + + * _FOREACH_REVERSE - - - + + * _FOREACH_REVERSE_FROM - - - + + * _FOREACH_REVERSE_SAFE - - - + + * _FOREACH_REVERSE_FROM_SAFE - - - + + * _INSERT_HEAD + + + + + * _INSERT_BEFORE - + - + + * _INSERT_AFTER + + + + + * _INSERT_TAIL - - + + + * _CONCAT s s + + + * _REMOVE_AFTER + - + - + * _REMOVE_HEAD + + + + + * _REMOVE s + s + + * _REPLACE - + - + + * _SWAP + + + + + * + */ +#ifdef QUEUE_MACRO_DEBUG +#warn Use QUEUE_MACRO_DEBUG_TRACE and/or QUEUE_MACRO_DEBUG_TRASH +#define QUEUE_MACRO_DEBUG_TRACE +#define QUEUE_MACRO_DEBUG_TRASH +#endif + +#ifdef QUEUE_MACRO_DEBUG_TRACE +/* Store the last 2 places the queue element or head was altered */ +struct qm_trace { + unsigned long lastline; + unsigned long prevline; + const char *lastfile; + const char *prevfile; +}; + +#define TRACEBUF struct qm_trace trace; +#define TRACEBUF_INITIALIZER { __LINE__, 0, __FILE__, NULL } , + +#define QMD_TRACE_HEAD(head) do { \ + (head)->trace.prevline = (head)->trace.lastline; \ + (head)->trace.prevfile = (head)->trace.lastfile; \ + (head)->trace.lastline = __LINE__; \ + (head)->trace.lastfile = __FILE__; \ +} while (0) + +#define QMD_TRACE_ELEM(elem) do { \ + (elem)->trace.prevline = (elem)->trace.lastline; \ + (elem)->trace.prevfile = (elem)->trace.lastfile; \ + (elem)->trace.lastline = __LINE__; \ + (elem)->trace.lastfile = __FILE__; \ +} while (0) + +#else /* !QUEUE_MACRO_DEBUG_TRACE */ +#define QMD_TRACE_ELEM(elem) +#define QMD_TRACE_HEAD(head) +#define TRACEBUF +#define TRACEBUF_INITIALIZER +#endif /* QUEUE_MACRO_DEBUG_TRACE */ + +#ifdef QUEUE_MACRO_DEBUG_TRASH +#define QMD_SAVELINK(name, link) void **name = (void *)&(link) +#define TRASHIT(x) do {(x) = (void *)-1;} while (0) +#define QMD_IS_TRASHED(x) ((x) == (void *)(intptr_t)-1) +#else /* !QUEUE_MACRO_DEBUG_TRASH */ +#define QMD_SAVELINK(name, link) +#define TRASHIT(x) +#define QMD_IS_TRASHED(x) 0 +#endif /* QUEUE_MACRO_DEBUG_TRASH */ + +#ifdef __cplusplus +/* + * In C++ there can be structure lists and class lists: + */ +#define QUEUE_TYPEOF(type) type +#else +#define QUEUE_TYPEOF(type) struct type +#endif + +/* + * Singly-linked List declarations. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_CLASS_HEAD(name, type) \ +struct name { \ + class type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +#define SLIST_CLASS_ENTRY(type) \ +struct { \ + class type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List functions. + */ +#if (defined(_KERNEL) && defined(INVARIANTS)) +#define QMD_SLIST_CHECK_PREVPTR(prevp, elm) do { \ + if (*(prevp) != (elm)) \ + panic("Bad prevptr *(%p) == %p != %p", \ + (prevp), *(prevp), (elm)); \ +} while (0) +#else +#define QMD_SLIST_CHECK_PREVPTR(prevp, elm) +#endif + +#define SLIST_CONCAT(head1, head2, type, field) do { \ + QUEUE_TYPEOF(type) *curelm = SLIST_FIRST(head1); \ + if (curelm == NULL) { \ + if ((SLIST_FIRST(head1) = SLIST_FIRST(head2)) != NULL) \ + SLIST_INIT(head2); \ + } else if (SLIST_FIRST(head2) != NULL) { \ + while (SLIST_NEXT(curelm, field) != NULL) \ + curelm = SLIST_NEXT(curelm, field); \ + SLIST_NEXT(curelm, field) = SLIST_FIRST(head2); \ + SLIST_INIT(head2); \ + } \ +} while (0) + +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) + +#define SLIST_EMPTY_ATOMIC(head) \ + (atomic_load_ptr(&(head)->slh_first) == NULL) + +#define SLIST_FIRST(head) ((head)->slh_first) + +#define SLIST_FOREACH(var, head, field) \ + for ((var) = SLIST_FIRST((head)); \ + (var); \ + (var) = SLIST_NEXT((var), field)) + +#define SLIST_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \ + (var); \ + (var) = SLIST_NEXT((var), field)) + +#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SLIST_FIRST((head)); \ + (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define SLIST_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \ + (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \ + for ((varp) = &SLIST_FIRST((head)); \ + ((var) = *(varp)) != NULL; \ + (varp) = &SLIST_NEXT((var), field)) + +#define SLIST_INIT(head) do { \ + SLIST_FIRST((head)) = NULL; \ +} while (0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \ + SLIST_NEXT((slistelm), field) = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \ + SLIST_FIRST((head)) = (elm); \ +} while (0) + +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if (SLIST_FIRST((head)) == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + QUEUE_TYPEOF(type) *curelm = SLIST_FIRST(head); \ + while (SLIST_NEXT(curelm, field) != (elm)) \ + curelm = SLIST_NEXT(curelm, field); \ + SLIST_REMOVE_AFTER(curelm, field); \ + } \ +} while (0) + +#define SLIST_REMOVE_AFTER(elm, field) do { \ + QMD_SAVELINK(oldnext, SLIST_NEXT(elm, field)->field.sle_next); \ + SLIST_NEXT(elm, field) = \ + SLIST_NEXT(SLIST_NEXT(elm, field), field); \ + TRASHIT(*oldnext); \ +} while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + QMD_SAVELINK(oldnext, SLIST_FIRST(head)->field.sle_next); \ + SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \ + TRASHIT(*oldnext); \ +} while (0) + +#define SLIST_REMOVE_PREVPTR(prevp, elm, field) do { \ + QMD_SLIST_CHECK_PREVPTR(prevp, elm); \ + *(prevp) = SLIST_NEXT(elm, field); \ + TRASHIT((elm)->field.sle_next); \ +} while (0) + +#define SLIST_SWAP(head1, head2, type) do { \ + QUEUE_TYPEOF(type) *swap_first = SLIST_FIRST(head1); \ + SLIST_FIRST(head1) = SLIST_FIRST(head2); \ + SLIST_FIRST(head2) = swap_first; \ +} while (0) + +#define SLIST_END(head) NULL + +/* + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type) \ +struct name { \ + struct type *stqh_first;/* first element */ \ + struct type **stqh_last;/* addr of last next element */ \ +} + +#define STAILQ_CLASS_HEAD(name, type) \ +struct name { \ + class type *stqh_first; /* first element */ \ + class type **stqh_last; /* addr of last next element */ \ +} + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type) \ +struct { \ + struct type *stqe_next; /* next element */ \ +} + +#define STAILQ_CLASS_ENTRY(type) \ +struct { \ + class type *stqe_next; /* next element */ \ +} + +/* + * Singly-linked Tail queue functions. + */ +#if (defined(_KERNEL) && defined(INVARIANTS)) +/* + * QMD_STAILQ_CHECK_EMPTY(STAILQ_HEAD *head) + * + * Validates that the stailq head's pointer to the last element's next pointer + * actually points to the head's first element pointer field. + */ +#define QMD_STAILQ_CHECK_EMPTY(head) do { \ + if ((head)->stqh_last != &(head)->stqh_first) \ + panic("Empty stailq %p->stqh_last is %p, not head's " \ + "first field address", (head), (head)->stqh_last); \ +} while (0) + +#define STAILQ_ASSERT_EMPTY(head) do { \ + if (!STAILQ_EMPTY((head))) \ + panic("stailq %p is not empty", (head)); \ +} while (0) + +/* + * QMD_STAILQ_CHECK_TAIL(STAILQ_HEAD *head) + * + * Validates that the stailq's last element's next pointer is NULL. + */ +#define QMD_STAILQ_CHECK_TAIL(head) do { \ + if (*(head)->stqh_last != NULL) \ + panic("Stailq %p last element's next pointer is %p, " \ + "not NULL", (head), *(head)->stqh_last); \ +} while (0) +#else +#define QMD_STAILQ_CHECK_EMPTY(head) +#define STAILQ_ASSERT_EMPTY(head) +#define QMD_STAILQ_CHECK_TAIL(head) +#endif /* (_KERNEL && INVARIANTS) */ + +#define STAILQ_CONCAT(head1, head2) do { \ + if (!STAILQ_EMPTY((head2))) { \ + *(head1)->stqh_last = (head2)->stqh_first; \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_INIT((head2)); \ + } \ +} while (0) + +#define STAILQ_EMPTY(head) ({ \ + if (STAILQ_FIRST(head) == NULL) \ + QMD_STAILQ_CHECK_EMPTY(head); \ + STAILQ_FIRST(head) == NULL; \ +}) + +#define STAILQ_EMPTY_ATOMIC(head) \ + (atomic_load_ptr(&(head)->stqh_first) == NULL) + +#define STAILQ_FIRST(head) ((head)->stqh_first) + +#define STAILQ_FOREACH(var, head, field) \ + for((var) = STAILQ_FIRST((head)); \ + (var); \ + (var) = STAILQ_NEXT((var), field)) + +#define STAILQ_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \ + (var); \ + (var) = STAILQ_NEXT((var), field)) + +#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = STAILQ_FIRST((head)); \ + (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \ + (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define STAILQ_INIT(head) do { \ + STAILQ_FIRST((head)) = NULL; \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_NEXT((tqelm), field) = (elm); \ +} while (0) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_FIRST((head)) = (elm); \ +} while (0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + QMD_STAILQ_CHECK_TAIL(head); \ + STAILQ_NEXT((elm), field) = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#define STAILQ_LAST(head, type, field) \ + (STAILQ_EMPTY((head)) ? NULL : \ + __containerof((head)->stqh_last, \ + QUEUE_TYPEOF(type), field.stqe_next)) + +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.stqe_next); \ + if (STAILQ_FIRST((head)) == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } \ + else { \ + QUEUE_TYPEOF(type) *curelm = STAILQ_FIRST(head); \ + while (STAILQ_NEXT(curelm, field) != (elm)) \ + curelm = STAILQ_NEXT(curelm, field); \ + STAILQ_REMOVE_AFTER(head, curelm, field); \ + } \ + TRASHIT(*oldnext); \ +} while (0) + +#define STAILQ_REMOVE_AFTER(head, elm, field) do { \ + if ((STAILQ_NEXT(elm, field) = \ + STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if ((STAILQ_FIRST((head)) = \ + STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_SWAP(head1, head2, type) do { \ + QUEUE_TYPEOF(type) *swap_first = STAILQ_FIRST(head1); \ + QUEUE_TYPEOF(type) **swap_last = (head1)->stqh_last; \ + STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_FIRST(head2) = swap_first; \ + (head2)->stqh_last = swap_last; \ + if (STAILQ_FIRST(head1) == NULL) \ + (head1)->stqh_last = &STAILQ_FIRST(head1); \ + if (STAILQ_FIRST(head2) == NULL) \ + (head2)->stqh_last = &STAILQ_FIRST(head2); \ +} while (0) + +#define STAILQ_END(head) NULL + + +/* + * List declarations. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_CLASS_HEAD(name, type) \ +struct name { \ + class type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +#define LIST_CLASS_ENTRY(type) \ +struct { \ + class type *le_next; /* next element */ \ + class type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ + +#if (defined(_KERNEL) && defined(INVARIANTS)) +/* + * QMD_LIST_CHECK_HEAD(LIST_HEAD *head, LIST_ENTRY NAME) + * + * If the list is non-empty, validates that the first element of the list + * points back at 'head.' + */ +#define QMD_LIST_CHECK_HEAD(head, field) do { \ + if (LIST_FIRST((head)) != NULL && \ + LIST_FIRST((head))->field.le_prev != \ + &LIST_FIRST((head))) \ + panic("Bad list head %p first->prev != head", (head)); \ +} while (0) + +/* + * QMD_LIST_CHECK_NEXT(TYPE *elm, LIST_ENTRY NAME) + * + * If an element follows 'elm' in the list, validates that the next element + * points back at 'elm.' + */ +#define QMD_LIST_CHECK_NEXT(elm, field) do { \ + if (LIST_NEXT((elm), field) != NULL && \ + LIST_NEXT((elm), field)->field.le_prev != \ + &((elm)->field.le_next)) \ + panic("Bad link elm %p next->prev != elm", (elm)); \ +} while (0) + +/* + * QMD_LIST_CHECK_PREV(TYPE *elm, LIST_ENTRY NAME) + * + * Validates that the previous element (or head of the list) points to 'elm.' + */ +#define QMD_LIST_CHECK_PREV(elm, field) do { \ + if (*(elm)->field.le_prev != (elm)) \ + panic("Bad link elm %p prev->next != elm", (elm)); \ +} while (0) +#else +#define QMD_LIST_CHECK_HEAD(head, field) +#define QMD_LIST_CHECK_NEXT(elm, field) +#define QMD_LIST_CHECK_PREV(elm, field) +#endif /* (_KERNEL && INVARIANTS) */ + +#define LIST_CONCAT(head1, head2, type, field) do { \ + QUEUE_TYPEOF(type) *curelm = LIST_FIRST(head1); \ + if (curelm == NULL) { \ + if ((LIST_FIRST(head1) = LIST_FIRST(head2)) != NULL) { \ + LIST_FIRST(head2)->field.le_prev = \ + &LIST_FIRST((head1)); \ + LIST_INIT(head2); \ + } \ + } else if (LIST_FIRST(head2) != NULL) { \ + while (LIST_NEXT(curelm, field) != NULL) \ + curelm = LIST_NEXT(curelm, field); \ + LIST_NEXT(curelm, field) = LIST_FIRST(head2); \ + LIST_FIRST(head2)->field.le_prev = &LIST_NEXT(curelm, field);\ + LIST_INIT(head2); \ + } \ +} while (0) + +#define LIST_EMPTY(head) ((head)->lh_first == NULL) + +#define LIST_EMPTY_ATOMIC(head) \ + (atomic_load_ptr(&(head)->lh_first) == NULL) + +#define LIST_FIRST(head) ((head)->lh_first) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = LIST_FIRST((head)); \ + (var); \ + (var) = LIST_NEXT((var), field)) + +#define LIST_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : LIST_FIRST((head))); \ + (var); \ + (var) = LIST_NEXT((var), field)) + +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST((head)); \ + (var) && ((tvar) = LIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define LIST_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : LIST_FIRST((head))); \ + (var) && ((tvar) = LIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define LIST_INIT(head) do { \ + LIST_FIRST((head)) = NULL; \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + QMD_LIST_CHECK_NEXT(listelm, field); \ + if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\ + LIST_NEXT((listelm), field)->field.le_prev = \ + &LIST_NEXT((elm), field); \ + LIST_NEXT((listelm), field) = (elm); \ + (elm)->field.le_prev = &LIST_NEXT((listelm), field); \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + QMD_LIST_CHECK_PREV(listelm, field); \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + LIST_NEXT((elm), field) = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &LIST_NEXT((elm), field); \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + QMD_LIST_CHECK_HEAD((head), field); \ + if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ + LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\ + LIST_FIRST((head)) = (elm); \ + (elm)->field.le_prev = &LIST_FIRST((head)); \ +} while (0) + +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_PREV(elm, head, type, field) \ + ((elm)->field.le_prev == &LIST_FIRST((head)) ? NULL : \ + __containerof((elm)->field.le_prev, \ + QUEUE_TYPEOF(type), field.le_next)) + +#define LIST_REMOVE_HEAD(head, field) \ + LIST_REMOVE(LIST_FIRST(head), field) + +#define LIST_REMOVE(elm, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.le_next); \ + QMD_SAVELINK(oldprev, (elm)->field.le_prev); \ + QMD_LIST_CHECK_NEXT(elm, field); \ + QMD_LIST_CHECK_PREV(elm, field); \ + if (LIST_NEXT((elm), field) != NULL) \ + LIST_NEXT((elm), field)->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = LIST_NEXT((elm), field); \ + TRASHIT(*oldnext); \ + TRASHIT(*oldprev); \ +} while (0) + +#define LIST_REPLACE(elm, elm2, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.le_next); \ + QMD_SAVELINK(oldprev, (elm)->field.le_prev); \ + QMD_LIST_CHECK_NEXT(elm, field); \ + QMD_LIST_CHECK_PREV(elm, field); \ + LIST_NEXT((elm2), field) = LIST_NEXT((elm), field); \ + if (LIST_NEXT((elm2), field) != NULL) \ + LIST_NEXT((elm2), field)->field.le_prev = \ + &(elm2)->field.le_next; \ + (elm2)->field.le_prev = (elm)->field.le_prev; \ + *(elm2)->field.le_prev = (elm2); \ + TRASHIT(*oldnext); \ + TRASHIT(*oldprev); \ +} while (0) + +#define LIST_SWAP(head1, head2, type, field) do { \ + QUEUE_TYPEOF(type) *swap_tmp = LIST_FIRST(head1); \ + LIST_FIRST((head1)) = LIST_FIRST((head2)); \ + LIST_FIRST((head2)) = swap_tmp; \ + if ((swap_tmp = LIST_FIRST((head1))) != NULL) \ + swap_tmp->field.le_prev = &LIST_FIRST((head1)); \ + if ((swap_tmp = LIST_FIRST((head2))) != NULL) \ + swap_tmp->field.le_prev = &LIST_FIRST((head2)); \ +} while (0) + +#define LIST_END(head) NULL + +/* + * Tail queue declarations. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ + TRACEBUF \ +} + +#define TAILQ_CLASS_HEAD(name, type) \ +struct name { \ + class type *tqh_first; /* first element */ \ + class type **tqh_last; /* addr of last next element */ \ + TRACEBUF \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first, TRACEBUF_INITIALIZER } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ + TRACEBUF \ +} + +#define TAILQ_CLASS_ENTRY(type) \ +struct { \ + class type *tqe_next; /* next element */ \ + class type **tqe_prev; /* address of previous next element */ \ + TRACEBUF \ +} + +/* + * Tail queue functions. + */ +#if (defined(_KERNEL) && defined(INVARIANTS)) +/* + * QMD_TAILQ_CHECK_HEAD(TAILQ_HEAD *head, TAILQ_ENTRY NAME) + * + * If the tailq is non-empty, validates that the first element of the tailq + * points back at 'head.' + */ +#define QMD_TAILQ_CHECK_HEAD(head, field) do { \ + if (!TAILQ_EMPTY(head) && \ + TAILQ_FIRST((head))->field.tqe_prev != \ + &TAILQ_FIRST((head))) \ + panic("Bad tailq head %p first->prev != head", (head)); \ +} while (0) + +/* + * QMD_TAILQ_CHECK_TAIL(TAILQ_HEAD *head, TAILQ_ENTRY NAME) + * + * Validates that the tail of the tailq is a pointer to pointer to NULL. + */ +#define QMD_TAILQ_CHECK_TAIL(head, field) do { \ + if (*(head)->tqh_last != NULL) \ + panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); \ +} while (0) + +/* + * QMD_TAILQ_CHECK_NEXT(TYPE *elm, TAILQ_ENTRY NAME) + * + * If an element follows 'elm' in the tailq, validates that the next element + * points back at 'elm.' + */ +#define QMD_TAILQ_CHECK_NEXT(elm, field) do { \ + if (TAILQ_NEXT((elm), field) != NULL && \ + TAILQ_NEXT((elm), field)->field.tqe_prev != \ + &((elm)->field.tqe_next)) \ + panic("Bad link elm %p next->prev != elm", (elm)); \ +} while (0) + +/* + * QMD_TAILQ_CHECK_PREV(TYPE *elm, TAILQ_ENTRY NAME) + * + * Validates that the previous element (or head of the tailq) points to 'elm.' + */ +#define QMD_TAILQ_CHECK_PREV(elm, field) do { \ + if (*(elm)->field.tqe_prev != (elm)) \ + panic("Bad link elm %p prev->next != elm", (elm)); \ +} while (0) +#else +#define QMD_TAILQ_CHECK_HEAD(head, field) +#define QMD_TAILQ_CHECK_TAIL(head, headname) +#define QMD_TAILQ_CHECK_NEXT(elm, field) +#define QMD_TAILQ_CHECK_PREV(elm, field) +#endif /* (_KERNEL && INVARIANTS) */ + +#define TAILQ_CONCAT(head1, head2, field) do { \ + if (!TAILQ_EMPTY(head2)) { \ + *(head1)->tqh_last = (head2)->tqh_first; \ + (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ + (head1)->tqh_last = (head2)->tqh_last; \ + TAILQ_INIT((head2)); \ + QMD_TRACE_HEAD(head1); \ + QMD_TRACE_HEAD(head2); \ + } \ +} while (0) + +#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) + +#define TAILQ_EMPTY_ATOMIC(head) \ + (atomic_load_ptr(&(head)->tqh_first) == NULL) + +#define TAILQ_FIRST(head) ((head)->tqh_first) + +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = TAILQ_FIRST((head)); \ + (var); \ + (var) = TAILQ_NEXT((var), field)) + +#define TAILQ_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \ + (var); \ + (var) = TAILQ_NEXT((var), field)) + +#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = TAILQ_FIRST((head)); \ + (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \ + (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var); \ + (var) = TAILQ_PREV((var), headname, field)) + +#define TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field) \ + for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \ + (var); \ + (var) = TAILQ_PREV((var), headname, field)) + +#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ + (var) = (tvar)) + +#define TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar)\ + for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \ + (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ + (var) = (tvar)) + +#define TAILQ_INIT(head) do { \ + TAILQ_FIRST((head)) = NULL; \ + (head)->tqh_last = &TAILQ_FIRST((head)); \ + QMD_TRACE_HEAD(head); \ +} while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + QMD_TAILQ_CHECK_NEXT(listelm, field); \ + if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else { \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_HEAD(head); \ + } \ + TAILQ_NEXT((listelm), field) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \ + QMD_TRACE_ELEM(&(elm)->field); \ + QMD_TRACE_ELEM(&(listelm)->field); \ +} while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + QMD_TAILQ_CHECK_PREV(listelm, field); \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + TAILQ_NEXT((elm), field) = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_ELEM(&(elm)->field); \ + QMD_TRACE_ELEM(&(listelm)->field); \ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + QMD_TAILQ_CHECK_HEAD(head, field); \ + if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \ + TAILQ_FIRST((head))->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + TAILQ_FIRST((head)) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \ + QMD_TRACE_HEAD(head); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + QMD_TAILQ_CHECK_TAIL(head, field); \ + TAILQ_NEXT((elm), field) = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_HEAD(head); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) + +/* + * The FAST function is fast in that it causes no data access other + * then the access to the head. The standard LAST function above + * will cause a data access of both the element you want and + * the previous element. FAST is very useful for instances when + * you may want to prefetch the last data element. + */ +#define TAILQ_LAST_FAST(head, type, field) \ + (TAILQ_EMPTY(head) ? NULL : __containerof((head)->tqh_last, QUEUE_TYPEOF(type), field.tqe_next)) + +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) + +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) + +#define TAILQ_PREV_FAST(elm, head, type, field) \ + ((elm)->field.tqe_prev == &(head)->tqh_first ? NULL : \ + __containerof((elm)->field.tqe_prev, QUEUE_TYPEOF(type), field.tqe_next)) + +#define TAILQ_REMOVE_HEAD(head, field) \ + TAILQ_REMOVE(head, TAILQ_FIRST(head), field) + +#define TAILQ_REMOVE(head, elm, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.tqe_next); \ + QMD_SAVELINK(oldprev, (elm)->field.tqe_prev); \ + QMD_TAILQ_CHECK_NEXT(elm, field); \ + QMD_TAILQ_CHECK_PREV(elm, field); \ + if ((TAILQ_NEXT((elm), field)) != NULL) \ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else { \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + QMD_TRACE_HEAD(head); \ + } \ + *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \ + TRASHIT(*oldnext); \ + TRASHIT(*oldprev); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_REPLACE(head, elm, elm2, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.tqe_next); \ + QMD_SAVELINK(oldprev, (elm)->field.tqe_prev); \ + QMD_TAILQ_CHECK_NEXT(elm, field); \ + QMD_TAILQ_CHECK_PREV(elm, field); \ + TAILQ_NEXT((elm2), field) = TAILQ_NEXT((elm), field); \ + if (TAILQ_NEXT((elm2), field) != TAILQ_END(head)) \ + TAILQ_NEXT((elm2), field)->field.tqe_prev = \ + &(elm2)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm2)->field.tqe_next; \ + (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ + *(elm2)->field.tqe_prev = (elm2); \ + TRASHIT(*oldnext); \ + TRASHIT(*oldprev); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_SWAP(head1, head2, type, field) do { \ + QUEUE_TYPEOF(type) *swap_first = (head1)->tqh_first; \ + QUEUE_TYPEOF(type) **swap_last = (head1)->tqh_last; \ + (head1)->tqh_first = (head2)->tqh_first; \ + (head1)->tqh_last = (head2)->tqh_last; \ + (head2)->tqh_first = swap_first; \ + (head2)->tqh_last = swap_last; \ + if ((swap_first = (head1)->tqh_first) != NULL) \ + swap_first->field.tqe_prev = &(head1)->tqh_first; \ + else \ + (head1)->tqh_last = &(head1)->tqh_first; \ + if ((swap_first = (head2)->tqh_first) != NULL) \ + swap_first->field.tqe_prev = &(head2)->tqh_first; \ + else \ + (head2)->tqh_last = &(head2)->tqh_first; \ +} while (0) + +#define TAILQ_END(head) NULL + +#endif /* !_SYS_QUEUE_H_ */ diff --git a/security/lua/tree.h b/security/lua/tree.h new file mode 100644 index 000000000000..c11bccfb387c --- /dev/null +++ b/security/lua/tree.h @@ -0,0 +1,1062 @@ +/* $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $ */ +/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */ + +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright 2002 Niels Provos + * 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. + * + * 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. + */ + +#ifndef _SYS_TREE_H_ +#define _SYS_TREE_H_ + +#include + +/* + * This file defines data structures for different types of trees: + * splay trees and rank-balanced trees. + * + * A splay tree is a self-organizing data structure. Every operation + * on the tree causes a splay to happen. The splay moves the requested + * node to the root of the tree and partly rebalances it. + * + * This has the benefit that request locality causes faster lookups as + * the requested nodes move to the top of the tree. On the other hand, + * every lookup causes memory writes. + * + * The Balance Theorem bounds the total access time for m operations + * and n inserts on an initially empty tree as O((m + n)lg n). The + * amortized cost for a sequence of m accesses to a splay tree is O(lg n); + * + * A rank-balanced tree is a binary search tree with an integer + * rank-difference as an attribute of each pointer from parent to child. + * The sum of the rank-differences on any path from a node down to null is + * the same, and defines the rank of that node. The rank of the null node + * is -1. + * + * Different additional conditions define different sorts of balanced trees, + * including "red-black" and "AVL" trees. The set of conditions applied here + * are the "weak-AVL" conditions of Haeupler, Sen and Tarjan presented in in + * "Rank Balanced Trees", ACM Transactions on Algorithms Volume 11 Issue 4 June + * 2015 Article No.: 30pp 1–26 https://doi.org/10.1145/2689412 (the HST paper): + * - every rank-difference is 1 or 2. + * - the rank of any leaf is 1. + * + * For historical reasons, rank differences that are even are associated + * with the color red (Rank-Even-Difference), and the child that a red edge + * points to is called a red child. + * + * Every operation on a rank-balanced tree is bounded as O(lg n). + * The maximum height of a rank-balanced tree is 2lg (n+1). + */ + +#define SPLAY_HEAD(name, type) \ +struct name { \ + struct type *sph_root; /* root of the tree */ \ +} + +#define SPLAY_INITIALIZER(root) \ + { NULL } + +#define SPLAY_INIT(root) do { \ + (root)->sph_root = NULL; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ENTRY(type) \ +struct { \ + struct type *spe_left; /* left element */ \ + struct type *spe_right; /* right element */ \ +} + +#define SPLAY_LEFT(elm, field) (elm)->field.spe_left +#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right +#define SPLAY_ROOT(head) (head)->sph_root +#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) + +/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ +#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_LINKLEFT(head, tmp, field) do { \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_LINKRIGHT(head, tmp, field) do { \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ + SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ + SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ +} while (/*CONSTCOND*/ 0) + +/* Generates prototypes and inline functions */ + +#define SPLAY_PROTOTYPE(name, type, field, cmp) \ +void name##_SPLAY(struct name *, struct type *); \ +void name##_SPLAY_MINMAX(struct name *, int); \ +struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ +struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ + \ +/* Finds the node with the same key as elm */ \ +static __unused __inline struct type * \ +name##_SPLAY_FIND(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) \ + return(NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) \ + return (head->sph_root); \ + return (NULL); \ +} \ + \ +static __unused __inline struct type * \ +name##_SPLAY_NEXT(struct name *head, struct type *elm) \ +{ \ + name##_SPLAY(head, elm); \ + if (SPLAY_RIGHT(elm, field) != NULL) { \ + elm = SPLAY_RIGHT(elm, field); \ + while (SPLAY_LEFT(elm, field) != NULL) { \ + elm = SPLAY_LEFT(elm, field); \ + } \ + } else \ + elm = NULL; \ + return (elm); \ +} \ + \ +static __unused __inline struct type * \ +name##_SPLAY_MIN_MAX(struct name *head, int val) \ +{ \ + name##_SPLAY_MINMAX(head, val); \ + return (SPLAY_ROOT(head)); \ +} + +/* Main splay operation. + * Moves node close to the key of elm to top + */ +#define SPLAY_GENERATE(name, type, field, cmp) \ +struct type * \ +name##_SPLAY_INSERT(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) { \ + SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ + } else { \ + __typeof(cmp(NULL, NULL)) __comp; \ + name##_SPLAY(head, elm); \ + __comp = (cmp)(elm, (head)->sph_root); \ + if (__comp < 0) { \ + SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ + SPLAY_RIGHT(elm, field) = (head)->sph_root; \ + SPLAY_LEFT((head)->sph_root, field) = NULL; \ + } else if (__comp > 0) { \ + SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT(elm, field) = (head)->sph_root; \ + SPLAY_RIGHT((head)->sph_root, field) = NULL; \ + } else \ + return ((head)->sph_root); \ + } \ + (head)->sph_root = (elm); \ + return (NULL); \ +} \ + \ +struct type * \ +name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *__tmp; \ + if (SPLAY_EMPTY(head)) \ + return (NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) { \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ + } else { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ + name##_SPLAY(head, elm); \ + SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ + } \ + return (elm); \ + } \ + return (NULL); \ +} \ + \ +void \ +name##_SPLAY(struct name *head, struct type *elm) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ + __typeof(cmp(NULL, NULL)) __comp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) > 0){ \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} \ + \ +/* Splay with either the minimum or the maximum element \ + * Used to find minimum or maximum element in tree. \ + */ \ +void name##_SPLAY_MINMAX(struct name *head, int __comp) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while (1) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp > 0) { \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} + +#define SPLAY_NEGINF -1 +#define SPLAY_INF 1 + +#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) +#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) +#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) +#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) +#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) +#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) + +#define SPLAY_FOREACH(x, name, head) \ + for ((x) = SPLAY_MIN(name, head); \ + (x) != NULL; \ + (x) = SPLAY_NEXT(name, head, x)) + +/* Macros that define a rank-balanced tree */ +#define RB_HEAD(name, type) \ +struct name { \ + struct type *rbh_root; /* root of the tree */ \ +} + +#define RB_INITIALIZER(root) \ + { NULL } + +#define RB_INIT(root) do { \ + (root)->rbh_root = NULL; \ +} while (/*CONSTCOND*/ 0) + +#define RB_ENTRY(type) \ +struct { \ + struct type *rbe_link[3]; \ +} + +/* + * With the expectation that any object of struct type has an + * address that is a multiple of 4, and that therefore the + * 2 least significant bits of a pointer to struct type are + * always zero, this implementation sets those bits to indicate + * that the left or right child of the tree node is "red". + */ +#define _RB_LINK(elm, dir, field) (elm)->field.rbe_link[dir] +#define _RB_UP(elm, field) _RB_LINK(elm, 0, field) +#define _RB_L ((__uintptr_t)1) +#define _RB_R ((__uintptr_t)2) +#define _RB_LR ((__uintptr_t)3) +#define _RB_BITS(elm) (*(__uintptr_t *)&elm) +#define _RB_BITSUP(elm, field) _RB_BITS(_RB_UP(elm, field)) +#define _RB_PTR(elm) (__typeof(elm)) \ + ((__uintptr_t)elm & ~_RB_LR) + +#define RB_PARENT(elm, field) _RB_PTR(_RB_UP(elm, field)) +#define RB_LEFT(elm, field) _RB_LINK(elm, _RB_L, field) +#define RB_RIGHT(elm, field) _RB_LINK(elm, _RB_R, field) +#define RB_ROOT(head) (head)->rbh_root +#define RB_EMPTY(head) (RB_ROOT(head) == NULL) + +#define RB_SET_PARENT(dst, src, field) do { \ + _RB_BITSUP(dst, field) = (__uintptr_t)src | \ + (_RB_BITSUP(dst, field) & _RB_LR); \ +} while (/*CONSTCOND*/ 0) + +#define RB_SET(elm, parent, field) do { \ + _RB_UP(elm, field) = parent; \ + RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ +} while (/*CONSTCOND*/ 0) + +/* + * Either RB_AUGMENT or RB_AUGMENT_CHECK is invoked in a loop at the root of + * every modified subtree, from the bottom up to the root, to update augmented + * node data. RB_AUGMENT_CHECK returns true only when the update changes the + * node data, so that updating can be stopped short of the root when it returns + * false. + */ +#ifndef RB_AUGMENT_CHECK +#ifndef RB_AUGMENT +#define RB_AUGMENT_CHECK(x) 0 +#else +#define RB_AUGMENT_CHECK(x) (RB_AUGMENT(x), 1) +#endif +#endif + +#define RB_UPDATE_AUGMENT(elm, field) do { \ + __typeof(elm) rb_update_tmp = (elm); \ + while (RB_AUGMENT_CHECK(rb_update_tmp) && \ + (rb_update_tmp = RB_PARENT(rb_update_tmp, field)) != NULL) \ + ; \ +} while (0) + +#define RB_SWAP_CHILD(head, par, out, in, field) do { \ + if (par == NULL) \ + RB_ROOT(head) = (in); \ + else if ((out) == RB_LEFT(par, field)) \ + RB_LEFT(par, field) = (in); \ + else \ + RB_RIGHT(par, field) = (in); \ +} while (/*CONSTCOND*/ 0) + +/* + * RB_ROTATE macro partially restructures the tree to improve balance. In the + * case when dir is _RB_L, tmp is a right child of elm. After rotation, elm + * is a left child of tmp, and the subtree that represented the items between + * them, which formerly hung to the left of tmp now hangs to the right of elm. + * The parent-child relationship between elm and its former parent is not + * changed; where this macro once updated those fields, that is now left to the + * caller of RB_ROTATE to clean up, so that a pair of rotations does not twice + * update the same pair of pointer fields with distinct values. + */ +#define RB_ROTATE(elm, tmp, dir, field) do { \ + if ((_RB_LINK(elm, dir ^ _RB_LR, field) = \ + _RB_LINK(tmp, dir, field)) != NULL) \ + RB_SET_PARENT(_RB_LINK(tmp, dir, field), elm, field); \ + _RB_LINK(tmp, dir, field) = (elm); \ + RB_SET_PARENT(elm, tmp, field); \ +} while (/*CONSTCOND*/ 0) + +/* Generates prototypes and inline functions */ +#define RB_PROTOTYPE(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) +#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __unused static) +#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ + RB_PROTOTYPE_RANK(name, type, attr) \ + RB_PROTOTYPE_INSERT_COLOR(name, type, attr); \ + RB_PROTOTYPE_REMOVE_COLOR(name, type, attr); \ + RB_PROTOTYPE_INSERT_FINISH(name, type, attr); \ + RB_PROTOTYPE_INSERT(name, type, attr); \ + RB_PROTOTYPE_REMOVE(name, type, attr); \ + RB_PROTOTYPE_FIND(name, type, attr); \ + RB_PROTOTYPE_NFIND(name, type, attr); \ + RB_PROTOTYPE_NEXT(name, type, attr); \ + RB_PROTOTYPE_INSERT_NEXT(name, type, attr); \ + RB_PROTOTYPE_PREV(name, type, attr); \ + RB_PROTOTYPE_INSERT_PREV(name, type, attr); \ + RB_PROTOTYPE_MINMAX(name, type, attr); \ + RB_PROTOTYPE_REINSERT(name, type, attr); +#ifdef _RB_DIAGNOSTIC +#define RB_PROTOTYPE_RANK(name, type, attr) \ + attr int name##_RB_RANK(struct type *); +#else +#define RB_PROTOTYPE_RANK(name, type, attr) +#endif +#define RB_PROTOTYPE_INSERT_COLOR(name, type, attr) \ + attr struct type *name##_RB_INSERT_COLOR(struct name *, \ + struct type *, struct type *) +#define RB_PROTOTYPE_REMOVE_COLOR(name, type, attr) \ + attr struct type *name##_RB_REMOVE_COLOR(struct name *, \ + struct type *, struct type *) +#define RB_PROTOTYPE_REMOVE(name, type, attr) \ + attr struct type *name##_RB_REMOVE(struct name *, struct type *) +#define RB_PROTOTYPE_INSERT_FINISH(name, type, attr) \ + attr struct type *name##_RB_INSERT_FINISH(struct name *, \ + struct type *, struct type **, struct type *) +#define RB_PROTOTYPE_INSERT(name, type, attr) \ + attr struct type *name##_RB_INSERT(struct name *, struct type *) +#define RB_PROTOTYPE_FIND(name, type, attr) \ + attr struct type *name##_RB_FIND(struct name *, struct type *) +#define RB_PROTOTYPE_NFIND(name, type, attr) \ + attr struct type *name##_RB_NFIND(struct name *, struct type *) +#define RB_PROTOTYPE_NEXT(name, type, attr) \ + attr struct type *name##_RB_NEXT(struct type *) +#define RB_PROTOTYPE_INSERT_NEXT(name, type, attr) \ + attr struct type *name##_RB_INSERT_NEXT(struct name *, \ + struct type *, struct type *) +#define RB_PROTOTYPE_PREV(name, type, attr) \ + attr struct type *name##_RB_PREV(struct type *) +#define RB_PROTOTYPE_INSERT_PREV(name, type, attr) \ + attr struct type *name##_RB_INSERT_PREV(struct name *, \ + struct type *, struct type *) +#define RB_PROTOTYPE_MINMAX(name, type, attr) \ + attr struct type *name##_RB_MINMAX(struct name *, int) +#define RB_PROTOTYPE_REINSERT(name, type, attr) \ + attr struct type *name##_RB_REINSERT(struct name *, struct type *) + +/* Main rb operation. + * Moves node close to the key of elm to top + */ +#define RB_GENERATE(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp,) +#define RB_GENERATE_STATIC(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp, __unused static) +#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ + RB_GENERATE_RANK(name, type, field, attr) \ + RB_GENERATE_INSERT_COLOR(name, type, field, attr) \ + RB_GENERATE_REMOVE_COLOR(name, type, field, attr) \ + RB_GENERATE_INSERT_FINISH(name, type, field, attr) \ + RB_GENERATE_INSERT(name, type, field, cmp, attr) \ + RB_GENERATE_REMOVE(name, type, field, attr) \ + RB_GENERATE_FIND(name, type, field, cmp, attr) \ + RB_GENERATE_NFIND(name, type, field, cmp, attr) \ + RB_GENERATE_NEXT(name, type, field, attr) \ + RB_GENERATE_INSERT_NEXT(name, type, field, cmp, attr) \ + RB_GENERATE_PREV(name, type, field, attr) \ + RB_GENERATE_INSERT_PREV(name, type, field, cmp, attr) \ + RB_GENERATE_MINMAX(name, type, field, attr) \ + RB_GENERATE_REINSERT(name, type, field, cmp, attr) + +#ifdef _RB_DIAGNOSTIC +#ifndef RB_AUGMENT +#define _RB_AUGMENT_VERIFY(x) RB_AUGMENT_CHECK(x) +#else +#define _RB_AUGMENT_VERIFY(x) 0 +#endif +#define RB_GENERATE_RANK(name, type, field, attr) \ +/* \ + * Return the rank of the subtree rooted at elm, or -1 if the subtree \ + * is not rank-balanced, or has inconsistent augmentation data. + */ \ +attr int \ +name##_RB_RANK(struct type *elm) \ +{ \ + struct type *left, *right, *up; \ + int left_rank, right_rank; \ + \ + if (elm == NULL) \ + return (0); \ + up = _RB_UP(elm, field); \ + left = RB_LEFT(elm, field); \ + left_rank = ((_RB_BITS(up) & _RB_L) ? 2 : 1) + \ + name##_RB_RANK(left); \ + right = RB_RIGHT(elm, field); \ + right_rank = ((_RB_BITS(up) & _RB_R) ? 2 : 1) + \ + name##_RB_RANK(right); \ + if (left_rank != right_rank || \ + (left_rank == 2 && left == NULL && right == NULL) || \ + _RB_AUGMENT_VERIFY(elm)) \ + return (-1); \ + return (left_rank); \ +} +#else +#define RB_GENERATE_RANK(name, type, field, attr) +#endif + +#define RB_GENERATE_INSERT_COLOR(name, type, field, attr) \ +attr struct type * \ +name##_RB_INSERT_COLOR(struct name *head, \ + struct type *parent, struct type *elm) \ +{ \ + /* \ + * Initially, elm is a leaf. Either its parent was previously \ + * a leaf, with two black null children, or an interior node \ + * with a black non-null child and a red null child. The \ + * balance criterion "the rank of any leaf is 1" precludes the \ + * possibility of two red null children for the initial parent. \ + * So the first loop iteration cannot lead to accessing an \ + * uninitialized 'child', and a later iteration can only happen \ + * when a value has been assigned to 'child' in the previous \ + * one. \ + */ \ + struct type *child, *child_up, *gpar; \ + __uintptr_t elmdir, sibdir; \ + \ + do { \ + /* the rank of the tree rooted at elm grew */ \ + gpar = _RB_UP(parent, field); \ + elmdir = RB_RIGHT(parent, field) == elm ? _RB_R : _RB_L; \ + if (_RB_BITS(gpar) & elmdir) { \ + /* shorten the parent-elm edge to rebalance */ \ + _RB_BITSUP(parent, field) ^= elmdir; \ + return (NULL); \ + } \ + sibdir = elmdir ^ _RB_LR; \ + /* the other edge must change length */ \ + _RB_BITSUP(parent, field) ^= sibdir; \ + if ((_RB_BITS(gpar) & _RB_LR) == 0) { \ + /* both edges now short, retry from parent */ \ + child = elm; \ + elm = parent; \ + continue; \ + } \ + _RB_UP(parent, field) = gpar = _RB_PTR(gpar); \ + if (_RB_BITSUP(elm, field) & elmdir) { \ + /* \ + * Exactly one of the edges descending from elm \ + * is long. The long one is in the same \ + * direction as the edge from parent to elm, \ + * so change that by rotation. The edge from \ + * parent to z was shortened above. Shorten \ + * the long edge down from elm, and adjust \ + * other edge lengths based on the downward \ + * edges from 'child'. \ + * \ + * par par \ + * / \ / \ \ + * elm z / z \ + * / \ child \ + * / child / \ \ + * / / \ elm \ \ + * w / \ / \ y \ + * x y w \ \ + * x \ + */ \ + RB_ROTATE(elm, child, elmdir, field); \ + child_up = _RB_UP(child, field); \ + if (_RB_BITS(child_up) & sibdir) \ + _RB_BITSUP(parent, field) ^= elmdir; \ + if (_RB_BITS(child_up) & elmdir) \ + _RB_BITSUP(elm, field) ^= _RB_LR; \ + else \ + _RB_BITSUP(elm, field) ^= elmdir; \ + /* if child is a leaf, don't augment elm, \ + * since it is restored to be a leaf again. */ \ + if ((_RB_BITS(child_up) & _RB_LR) == 0) \ + elm = child; \ + } else \ + child = elm; \ + \ + /* \ + * The long edge descending from 'child' points back \ + * in the direction of 'parent'. Rotate to make \ + * 'parent' a child of 'child', then make both edges \ + * of 'child' short to rebalance. \ + * \ + * par child \ + * / \ / \ \ + * / z x par \ + * child / \ \ + * / \ / z \ + * x \ y \ + * y \ + */ \ + RB_ROTATE(parent, child, sibdir, field); \ + _RB_UP(child, field) = gpar; \ + RB_SWAP_CHILD(head, gpar, parent, child, field); \ + /* \ + * Elements rotated down have new, smaller subtrees, \ + * so update augmentation for them. \ + */ \ + if (elm != child) \ + (void)RB_AUGMENT_CHECK(elm); \ + (void)RB_AUGMENT_CHECK(parent); \ + return (child); \ + } while ((parent = gpar) != NULL); \ + return (NULL); \ +} + +#ifndef RB_STRICT_HST +/* + * In REMOVE_COLOR, the HST paper, in figure 3, in the single-rotate case, has + * 'parent' with one higher rank, and then reduces its rank if 'parent' has + * become a leaf. This implementation always has the parent in its new position + * with lower rank, to avoid the leaf check. Define RB_STRICT_HST to 1 to get + * the behavior that HST describes. + */ +#define RB_STRICT_HST 0 +#endif + +#define RB_GENERATE_REMOVE_COLOR(name, type, field, attr) \ +attr struct type * \ +name##_RB_REMOVE_COLOR(struct name *head, \ + struct type *parent, struct type *elm) \ +{ \ + struct type *gpar, *sib, *up; \ + __uintptr_t elmdir, sibdir; \ + \ + if (RB_RIGHT(parent, field) == elm && \ + RB_LEFT(parent, field) == elm) { \ + /* Deleting a leaf that is an only-child creates a \ + * rank-2 leaf. Demote that leaf. */ \ + _RB_UP(parent, field) = _RB_PTR(_RB_UP(parent, field)); \ + elm = parent; \ + if ((parent = _RB_UP(elm, field)) == NULL) \ + return (NULL); \ + } \ + do { \ + /* the rank of the tree rooted at elm shrank */ \ + gpar = _RB_UP(parent, field); \ + elmdir = RB_RIGHT(parent, field) == elm ? _RB_R : _RB_L; \ + _RB_BITS(gpar) ^= elmdir; \ + if (_RB_BITS(gpar) & elmdir) { \ + /* lengthen the parent-elm edge to rebalance */ \ + _RB_UP(parent, field) = gpar; \ + return (NULL); \ + } \ + if (_RB_BITS(gpar) & _RB_LR) { \ + /* shorten other edge, retry from parent */ \ + _RB_BITS(gpar) ^= _RB_LR; \ + _RB_UP(parent, field) = gpar; \ + gpar = _RB_PTR(gpar); \ + continue; \ + } \ + sibdir = elmdir ^ _RB_LR; \ + sib = _RB_LINK(parent, sibdir, field); \ + up = _RB_UP(sib, field); \ + _RB_BITS(up) ^= _RB_LR; \ + if ((_RB_BITS(up) & _RB_LR) == 0) { \ + /* shorten edges descending from sib, retry */ \ + _RB_UP(sib, field) = up; \ + continue; \ + } \ + if ((_RB_BITS(up) & sibdir) == 0) { \ + /* \ + * The edge descending from 'sib' away from \ + * 'parent' is long. The short edge descending \ + * from 'sib' toward 'parent' points to 'elm*' \ + * Rotate to make 'sib' a child of 'elm*' \ + * then adjust the lengths of the edges \ + * descending from 'sib' and 'elm*'. \ + * \ + * par par \ + * / \ / \ \ + * / sib elm \ \ + * / / \ elm* \ + * elm elm* \ / \ \ + * / \ \ / \ \ + * / \ z / \ \ + * x y x sib \ + * / \ \ + * / z \ + * y \ + */ \ + elm = _RB_LINK(sib, elmdir, field); \ + /* elm is a 1-child. First rotate at elm. */ \ + RB_ROTATE(sib, elm, sibdir, field); \ + up = _RB_UP(elm, field); \ + _RB_BITSUP(parent, field) ^= \ + (_RB_BITS(up) & elmdir) ? _RB_LR : elmdir; \ + _RB_BITSUP(sib, field) ^= \ + (_RB_BITS(up) & sibdir) ? _RB_LR : sibdir; \ + _RB_BITSUP(elm, field) |= _RB_LR; \ + } else { \ + if ((_RB_BITS(up) & elmdir) == 0 && \ + RB_STRICT_HST && elm != NULL) { \ + /* if parent does not become a leaf, \ + do not demote parent yet. */ \ + _RB_BITSUP(parent, field) ^= sibdir; \ + _RB_BITSUP(sib, field) ^= _RB_LR; \ + } else if ((_RB_BITS(up) & elmdir) == 0) { \ + /* demote parent. */ \ + _RB_BITSUP(parent, field) ^= elmdir; \ + _RB_BITSUP(sib, field) ^= sibdir; \ + } else \ + _RB_BITSUP(sib, field) ^= sibdir; \ + elm = sib; \ + } \ + \ + /* \ + * The edge descending from 'elm' away from 'parent' \ + * is short. Rotate to make 'parent' a child of 'elm', \ + * then lengthen the short edges descending from \ + * 'parent' and 'elm' to rebalance. \ + * \ + * par elm \ + * / \ / \ \ + * e \ / \ \ + * elm / \ \ + * / \ par s \ + * / \ / \ \ + * / \ e \ \ + * x s x \ + */ \ + RB_ROTATE(parent, elm, elmdir, field); \ + RB_SET_PARENT(elm, gpar, field); \ + RB_SWAP_CHILD(head, gpar, parent, elm, field); \ + /* \ + * An element rotated down, but not into the search \ + * path has a new, smaller subtree, so update \ + * augmentation for it. \ + */ \ + if (sib != elm) \ + (void)RB_AUGMENT_CHECK(sib); \ + return (parent); \ + } while (elm = parent, (parent = gpar) != NULL); \ + return (NULL); \ +} + +#define _RB_AUGMENT_WALK(elm, match, field) \ +do { \ + if (match == elm) \ + match = NULL; \ +} while (RB_AUGMENT_CHECK(elm) && \ + (elm = RB_PARENT(elm, field)) != NULL) + +#define RB_GENERATE_REMOVE(name, type, field, attr) \ +attr struct type * \ +name##_RB_REMOVE(struct name *head, struct type *out) \ +{ \ + struct type *child, *in, *opar, *parent; \ + \ + child = RB_LEFT(out, field); \ + in = RB_RIGHT(out, field); \ + opar = _RB_UP(out, field); \ + if (in == NULL || child == NULL) { \ + in = child = (in == NULL ? child : in); \ + parent = opar = _RB_PTR(opar); \ + } else { \ + parent = in; \ + while (RB_LEFT(in, field)) \ + in = RB_LEFT(in, field); \ + RB_SET_PARENT(child, in, field); \ + RB_LEFT(in, field) = child; \ + child = RB_RIGHT(in, field); \ + if (parent != in) { \ + RB_SET_PARENT(parent, in, field); \ + RB_RIGHT(in, field) = parent; \ + parent = RB_PARENT(in, field); \ + RB_LEFT(parent, field) = child; \ + } \ + _RB_UP(in, field) = opar; \ + opar = _RB_PTR(opar); \ + } \ + RB_SWAP_CHILD(head, opar, out, in, field); \ + if (child != NULL) \ + _RB_UP(child, field) = parent; \ + if (parent != NULL) { \ + opar = name##_RB_REMOVE_COLOR(head, parent, child); \ + /* if rotation has made 'parent' the root of the same \ + * subtree as before, don't re-augment it. */ \ + if (parent == in && RB_LEFT(parent, field) == NULL) { \ + opar = NULL; \ + parent = RB_PARENT(parent, field); \ + } \ + _RB_AUGMENT_WALK(parent, opar, field); \ + if (opar != NULL) { \ + /* \ + * Elements rotated into the search path have \ + * changed subtrees, so update augmentation for \ + * them if AUGMENT_WALK didn't. \ + */ \ + (void)RB_AUGMENT_CHECK(opar); \ + (void)RB_AUGMENT_CHECK(RB_PARENT(opar, field)); \ + } \ + } \ + return (out); \ +} + +#define RB_GENERATE_INSERT_FINISH(name, type, field, attr) \ +/* Inserts a node into the RB tree */ \ +attr struct type * \ +name##_RB_INSERT_FINISH(struct name *head, struct type *parent, \ + struct type **pptr, struct type *elm) \ +{ \ + struct type *tmp = NULL; \ + \ + RB_SET(elm, parent, field); \ + *pptr = elm; \ + if (parent != NULL) \ + tmp = name##_RB_INSERT_COLOR(head, parent, elm); \ + _RB_AUGMENT_WALK(elm, tmp, field); \ + if (tmp != NULL) \ + /* \ + * An element rotated into the search path has a \ + * changed subtree, so update augmentation for it if \ + * AUGMENT_WALK didn't. \ + */ \ + (void)RB_AUGMENT_CHECK(tmp); \ + return (NULL); \ +} + +#define RB_GENERATE_INSERT(name, type, field, cmp, attr) \ +/* Inserts a node into the RB tree */ \ +attr struct type * \ +name##_RB_INSERT(struct name *head, struct type *elm) \ +{ \ + struct type *tmp; \ + struct type **tmpp = &RB_ROOT(head); \ + struct type *parent = NULL; \ + \ + while ((tmp = *tmpp) != NULL) { \ + parent = tmp; \ + __typeof(cmp(NULL, NULL)) comp = (cmp)(elm, parent); \ + if (comp < 0) \ + tmpp = &RB_LEFT(parent, field); \ + else if (comp > 0) \ + tmpp = &RB_RIGHT(parent, field); \ + else \ + return (parent); \ + } \ + return (name##_RB_INSERT_FINISH(head, parent, tmpp, elm)); \ +} + +#define RB_GENERATE_FIND(name, type, field, cmp, attr) \ +/* Finds the node with the same key as elm */ \ +attr struct type * \ +name##_RB_FIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + __typeof(cmp(NULL, NULL)) comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (NULL); \ +} + +#define RB_GENERATE_NFIND(name, type, field, cmp, attr) \ +/* Finds the first node greater than or equal to the search key */ \ +attr struct type * \ +name##_RB_NFIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *res = NULL; \ + __typeof(cmp(NULL, NULL)) comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) { \ + res = tmp; \ + tmp = RB_LEFT(tmp, field); \ + } \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (res); \ +} + +#define RB_GENERATE_NEXT(name, type, field, attr) \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_NEXT(struct type *elm) \ +{ \ + if (RB_RIGHT(elm, field)) { \ + elm = RB_RIGHT(elm, field); \ + while (RB_LEFT(elm, field)) \ + elm = RB_LEFT(elm, field); \ + } else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + return (elm); \ +} + +#if defined(_KERNEL) && defined(DIAGNOSTIC) +#define _RB_ORDER_CHECK(cmp, lo, hi) do { \ + KASSERT((cmp)(lo, hi) < 0, ("out of order insertion")); \ +} while (0) +#else +#define _RB_ORDER_CHECK(cmp, lo, hi) do {} while (0) +#endif + +#define RB_GENERATE_INSERT_NEXT(name, type, field, cmp, attr) \ +/* Inserts a node into the next position in the RB tree */ \ +attr struct type * \ +name##_RB_INSERT_NEXT(struct name *head, \ + struct type *elm, struct type *next) \ +{ \ + struct type *tmp; \ + struct type **tmpp = &RB_RIGHT(elm, field); \ + \ + _RB_ORDER_CHECK(cmp, elm, next); \ + if (name##_RB_NEXT(elm) != NULL) \ + _RB_ORDER_CHECK(cmp, next, name##_RB_NEXT(elm)); \ + while ((tmp = *tmpp) != NULL) { \ + elm = tmp; \ + tmpp = &RB_LEFT(elm, field); \ + } \ + return (name##_RB_INSERT_FINISH(head, elm, tmpp, next)); \ +} + +#define RB_GENERATE_PREV(name, type, field, attr) \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_PREV(struct type *elm) \ +{ \ + if (RB_LEFT(elm, field)) { \ + elm = RB_LEFT(elm, field); \ + while (RB_RIGHT(elm, field)) \ + elm = RB_RIGHT(elm, field); \ + } else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + return (elm); \ +} + +#define RB_GENERATE_INSERT_PREV(name, type, field, cmp, attr) \ +/* Inserts a node into the prev position in the RB tree */ \ +attr struct type * \ +name##_RB_INSERT_PREV(struct name *head, \ + struct type *elm, struct type *prev) \ +{ \ + struct type *tmp; \ + struct type **tmpp = &RB_LEFT(elm, field); \ + \ + _RB_ORDER_CHECK(cmp, prev, elm); \ + if (name##_RB_PREV(elm) != NULL) \ + _RB_ORDER_CHECK(cmp, name##_RB_PREV(elm), prev); \ + while ((tmp = *tmpp) != NULL) { \ + elm = tmp; \ + tmpp = &RB_RIGHT(elm, field); \ + } \ + return (name##_RB_INSERT_FINISH(head, elm, tmpp, prev)); \ +} + +#define RB_GENERATE_MINMAX(name, type, field, attr) \ +attr struct type * \ +name##_RB_MINMAX(struct name *head, int val) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *parent = NULL; \ + while (tmp) { \ + parent = tmp; \ + if (val < 0) \ + tmp = RB_LEFT(tmp, field); \ + else \ + tmp = RB_RIGHT(tmp, field); \ + } \ + return (parent); \ +} + +#define RB_GENERATE_REINSERT(name, type, field, cmp, attr) \ +attr struct type * \ +name##_RB_REINSERT(struct name *head, struct type *elm) \ +{ \ + struct type *cmpelm; \ + if (((cmpelm = RB_PREV(name, head, elm)) != NULL && \ + cmp(cmpelm, elm) >= 0) || \ + ((cmpelm = RB_NEXT(name, head, elm)) != NULL && \ + cmp(elm, cmpelm) >= 0)) { \ + /* XXXLAS: Remove/insert is heavy handed. */ \ + RB_REMOVE(name, head, elm); \ + return (RB_INSERT(name, head, elm)); \ + } \ + return (NULL); \ +} \ + +#define RB_NEGINF -1 +#define RB_INF 1 + +#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) +#define RB_INSERT_NEXT(name, x, y, z) name##_RB_INSERT_NEXT(x, y, z) +#define RB_INSERT_PREV(name, x, y, z) name##_RB_INSERT_PREV(x, y, z) +#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) +#define RB_FIND(name, x, y) name##_RB_FIND(x, y) +#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) +#define RB_NEXT(name, x, y) name##_RB_NEXT(y) +#define RB_PREV(name, x, y) name##_RB_PREV(y) +#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) +#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) +#define RB_REINSERT(name, x, y) name##_RB_REINSERT(x, y) + +#define RB_FOREACH(x, name, head) \ + for ((x) = RB_MIN(name, head); \ + (x) != NULL; \ + (x) = name##_RB_NEXT(x)) + +#define RB_FOREACH_FROM(x, name, y) \ + for ((x) = (y); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ + (x) = (y)) + +#define RB_FOREACH_SAFE(x, name, head, y) \ + for ((x) = RB_MIN(name, head); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ + (x) = (y)) + +#define RB_FOREACH_REVERSE(x, name, head) \ + for ((x) = RB_MAX(name, head); \ + (x) != NULL; \ + (x) = name##_RB_PREV(x)) + +#define RB_FOREACH_REVERSE_FROM(x, name, y) \ + for ((x) = (y); \ + ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \ + (x) = (y)) + +#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ + for ((x) = RB_MAX(name, head); \ + ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \ + (x) = (y)) + +#endif /* _SYS_TREE_H_ */ From 0edf337966e90992298d2eb24e7dd6d1f312508f Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Fri, 28 Mar 2025 16:02:22 +0800 Subject: [PATCH 015/110] lua-lsm: lib - fixes for build on linux Fixes for queue and tree --- security/lua/queue.h | 10 ++++------ security/lua/tree.h | 6 +++++- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/security/lua/queue.h b/security/lua/queue.h index 70d13ee92617..64084823d671 100644 --- a/security/lua/queue.h +++ b/security/lua/queue.h @@ -32,8 +32,6 @@ #ifndef _SYS_QUEUE_H_ #define _SYS_QUEUE_H_ -#include - /* * This file defines four types of data structures: singly-linked lists, * singly-linked tail queues, lists and tail queues. @@ -441,7 +439,7 @@ struct { \ #define STAILQ_LAST(head, type, field) \ (STAILQ_EMPTY((head)) ? NULL : \ - __containerof((head)->stqh_last, \ + container_of((head)->stqh_last, \ QUEUE_TYPEOF(type), field.stqe_next)) #define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) @@ -639,7 +637,7 @@ struct { \ #define LIST_PREV(elm, head, type, field) \ ((elm)->field.le_prev == &LIST_FIRST((head)) ? NULL : \ - __containerof((elm)->field.le_prev, \ + container_of((elm)->field.le_prev, \ QUEUE_TYPEOF(type), field.le_next)) #define LIST_REMOVE_HEAD(head, field) \ @@ -898,7 +896,7 @@ struct { \ * you may want to prefetch the last data element. */ #define TAILQ_LAST_FAST(head, type, field) \ - (TAILQ_EMPTY(head) ? NULL : __containerof((head)->tqh_last, QUEUE_TYPEOF(type), field.tqe_next)) + (TAILQ_EMPTY(head) ? NULL : container_of((head)->tqh_last, QUEUE_TYPEOF(type), field.tqe_next)) #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) @@ -907,7 +905,7 @@ struct { \ #define TAILQ_PREV_FAST(elm, head, type, field) \ ((elm)->field.tqe_prev == &(head)->tqh_first ? NULL : \ - __containerof((elm)->field.tqe_prev, QUEUE_TYPEOF(type), field.tqe_next)) + container_of((elm)->field.tqe_prev, QUEUE_TYPEOF(type), field.tqe_next)) #define TAILQ_REMOVE_HEAD(head, field) \ TAILQ_REMOVE(head, TAILQ_FIRST(head), field) diff --git a/security/lua/tree.h b/security/lua/tree.h index c11bccfb387c..025e74112b65 100644 --- a/security/lua/tree.h +++ b/security/lua/tree.h @@ -31,7 +31,11 @@ #ifndef _SYS_TREE_H_ #define _SYS_TREE_H_ -#include +#include + +typedef uintptr_t __uintptr_t; +#define __unused __maybe_unused + /* * This file defines data structures for different types of trees: From cbcd2446505e99e6fc94d78fabfb7d429e3445eb Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Fri, 22 Aug 2025 12:48:01 +0800 Subject: [PATCH 016/110] lua-lsm: add common auxiliary library Signed-off-by: Tianjia Zhang --- security/lua/auxlib.c | 302 ++++++++++++++++++++++++++++++++++++++++++ security/lua/auxlib.h | 66 +++++++++ security/lua/debug.h | 44 ++++++ 3 files changed, 412 insertions(+) create mode 100644 security/lua/auxlib.c create mode 100644 security/lua/auxlib.h create mode 100644 security/lua/debug.h diff --git a/security/lua/auxlib.c b/security/lua/auxlib.c new file mode 100644 index 000000000000..6e56f8f94408 --- /dev/null +++ b/security/lua/auxlib.c @@ -0,0 +1,302 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Lua based LSM + * + * Copyright (C) 2025 The Alibaba Cloud Linux Authors. + */ + +#define pr_fmt(fmt) "lua-lsm: " fmt + +#include "debug.h" +#include +#include +#include +#include +#include +#include +#include "auxlib.h" + + +void lua_stack_dump(lua_State *L) +{ + int top = lua_gettop(L); + int i; + + __log_info("-------- stack dump start --------\n"); + for (i = 1; i <= top; i++) { + int type = lua_type(L, i); + const char * __maybe_unused name = lua_typename(L, type); + + if (type == LUA_TSTRING) + __log_info(" %2d [%s] %s\n", i, name, lua_tostring(L, i)); + else + __log_info(" %2d [%s]\n", i, name); + } + __log_info("-------- stack dump end --------\n"); +} + +void lua_table_dump(lua_State *L, const char *prefix) +{ + int i = 1; + if (!lua_istable(L, -1)) { + __log_err("%s: not a table, type = %s\n", + prefix, luaL_typename(L, -1)); + return; + } + __log_info("-------- %s: table dump start --------\n", prefix); + /* table is in the stack at index 't' */ + lua_pushnil(L); /* first key */ + while (lua_next(L, -2) != 0) { + /* uses 'key' (at index -2) and 'value' (at index -1) */ + __log_info(" %2d: [%s] %s\t- [%s] %s\n", i, + luaL_typename(L, -2), lua_tostring(L, -2) ?: "(null)", + luaL_typename(L, -1), lua_tostring(L, -1) ?: "(null)"); + /* removes 'value'; keeps 'key' for next iteration */ + lua_pop(L, 1); + i += 1; + } + __log_info("-------- %s: table dump end --------\n", prefix); +} + +/* borrowed from lua.c */ +int lua_traceback(lua_State *L) +{ + __log_err("@_@ LuaVM: top = %d\n", lua_gettop(L)); + if (!lua_isstring(L, 1)) /* 'message' not a string? */ + return 1; /* keep it intact */ + __log_err("@_@ LuaVM: %s\n", lua_tostring(L, -1)); + lua_getfield(L, LUA_GLOBALSINDEX, "debug"); + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + return 1; + } + lua_getfield(L, -1, "traceback"); + if (!lua_isfunction(L, -1)) { + lua_pop(L, 2); + return 1; + } + lua_pushvalue(L, 1); /* pass error message */ + lua_pushinteger(L, 2); /* skip this function and traceback */ + lua_call(L, 2, 1); /* call debug.traceback */ + __log_err("@_@ LuaVM stack: %s\n", lua_tostring(L, -1)); + + __log_err("------------------ C stack dump start ------------------\n"); + dump_stack(); + __log_err("------------------ C stack dump end ------------------\n"); + return 1; +} + +int luaL_loadbuffer_wrap(lua_State *L, const char *buff, + size_t sz, const char *name) +{ + int status; + + status = luaL_loadbuffer(L, buff, sz, name); + if (status != 0) { + const char * __maybe_unused error = lua_tostring(L, -1); + int err; + __log_err("load: status = %d, top = %d, %s\n", + status, lua_gettop(L), error); + switch (status) { + case LUA_ERRMEM: err = -ENOMEM; break; + case LUA_ERRSYNTAX: err = -EDOM; break; + default: err = -EINVAL; break; + } + lua_pop(L, 1); + return err; + } + return 0; +} + +int lua_pcall_wrap(lua_State *L, int nargs, int nresults, int errfunc) +{ + int status; + + status = lua_pcall(L, nargs, nresults, errfunc); + if (status != 0) { + int err; + const char *error = lua_tostring(L, -1); + if (!error) + error = "unknown error"; + __log_err("pcall: status = %d, top = %d [%s]\n\t%s\n", + status, lua_gettop(L), luaL_typename(L, -1), error); + switch (status) { + case LUA_ERRMEM: err = -ENOMEM; break; + case LUA_ERRRUN: err = -ENOEXEC; break; + case LUA_ERRERR: err = -EFAULT; break; + default: err = -EINVAL; break; + } + lua_pop(L, 1); + return err; + } + return 0; +} + +/* + ** Stripped-down 'require': After checking "loaded" table, calls 'openf' + ** to open a module, registers the result in 'package.loaded' table and, + ** if 'glb' is true, also registers the result in the global table. + ** Leaves resulting module on the top. + */ +void luaL_requiref(lua_State *L, const char *modname, + lua_CFunction openf, int glb) +{ + luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 1); + lua_getfield(L, -1, modname); /* _LOADED[modname] */ + if (!lua_toboolean(L, -1)) { /* package not already loaded? */ + lua_pop(L, 1); /* remove field */ + lua_pushcfunction(L, openf); + lua_pushstring(L, modname); /* argument to open function */ + lua_call(L, 1, 1); /* call 'openf' to open module */ + lua_pushvalue(L, -1); /* make copy of module (call result) */ + lua_setfield(L, -3, modname); /* _LOADED[modname] = module */ + } + lua_remove(L, -2); /* remove _LOADED table */ + if (glb) { + lua_pushvalue(L, -1); /* copy of module */ + lua_setglobal(L, modname); /* _G[modname] = module */ + } +} + +void settopfenvfrom(lua_State *L, int from) +{ + lua_getfenv(L, from); + lua_setfenv(L, -2); +} + +unsigned int tocflags(lua_State *L, int idx, int top, + const struct cflag_opt *opts, unsigned int d) +{ + unsigned int flags = 0; + const char *s; + int k, i; + + if (top == -1) + top = idx; + + for ( ; idx <= top; idx++) { + switch (lua_type(L, idx)) { + case LUA_TSTRING: + s = lua_tostring(L, idx); + for (i = 0; opts[i].name; i++) { + if (strcasecmp(s, opts[i].name) != 0) + continue; + flags |= opts[i].flag; + break; + } + break; + + case LUA_TTABLE: + for (k = 1, lua_rawgeti(L, idx, k); + !lua_isnil(L, -1); + lua_pop(L, 1), lua_rawgeti(L, idx, ++k)) { + if (lua_type(L, -1) != LUA_TSTRING) + continue; + s = lua_tostring(L, -1); + for (i = 0; opts[i].name; i++) { + if (strcasecmp(s, opts[i].name) != 0) + continue; + flags |= opts[i].flag; + break; + } + } + lua_pop(L, 1); + break; + + case LUA_TNUMBER: + flags |= (unsigned int)lua_tointeger(L, idx); + break; + + case LUA_TBOOLEAN: + if (lua_toboolean(L, idx)) + break; + /* else fall through */ + fallthrough; + case LUA_TNONE: + case LUA_TNIL: + flags = d; /* default flags */ + break; + } + } + + return flags; +} + +const char * +fromcflags(const struct cflag_opt *opts, unsigned int flag, const char *d) +{ + int i; + for (i = 0; opts[i].name; i++) { + if (opts[i].flag == flag) + return opts[i].name; + } + return d; +} + +void table_fromopts(lua_State *L, const struct cflag_opt *opts, + unsigned int bitfield, unsigned int mask) +{ + int i; + int hw; + if (!bitfield) { + for (i = 0; opts[i].name; i++) + bitfield |= opts[i].flag; + } + hw = (int)hweight_long((unsigned long)(mask & bitfield)); + lua_createtable(L, 0, hw); + for (i = 0; opts[i].name; i++) { + if (mask & opts[i].flag) { + lua_pushboolean(L, 1); + lua_setfield(L, -2, opts[i].name); + } + } +} + +void **newcptr(lua_State *L, const char *metatable) +{ + void **p = (void **)lua_newuserdata(L, sizeof(void *)); + *p = NULL; + luaL_getmetatable(L, metatable); + lua_setmetatable(L, -2); + return p; +} + +void createmeta(lua_State *L, const char *name, + const luaL_Reg *meth, int index, int pop) +{ + luaL_newmetatable(L, name); + lua_pushstring(L, "cannot get a protected metatable"); + lua_setfield(L, -2, "__metatable"); /* metatable.__metatable = msg */ + if (index) { + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */ + } + if (meth) + luaL_register(L, NULL, meth); + if (pop) + lua_pop(L, 1); +} + +void *checkudata(lua_State *L, int ud, const char *name) +{ + void *p = lua_touserdata(L, ud); + if (p != NULL) { /* value is a userdata? */ + if (lua_getmetatable(L, ud)) { /* does it have a metatable? */ + lua_getfield(L, LUA_REGISTRYINDEX, name); /* get correct metatable */ + if (lua_rawequal(L, -1, -2)) { /* does it have the correct mt? */ + lua_pop(L, 2); /* remove both metatables */ + return p; + } + } + } + return NULL; +} + +void setconst(lua_State *L, const struct const_value *cv) +{ + for ( ; cv->name; cv++) { + lua_pushstring(L, cv->name); + lua_pushnumber(L, cv->value); + lua_settable(L, -3); + } +} diff --git a/security/lua/auxlib.h b/security/lua/auxlib.h new file mode 100644 index 000000000000..fb01da9958fb --- /dev/null +++ b/security/lua/auxlib.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Lua based LSM + * + * Copyright (C) 2025 The Alibaba Cloud Linux Authors. + */ + +#ifndef _SECURITY_LUA_LSM_AUXLIB_H +#define _SECURITY_LUA_LSM_AUXLIB_H + +#include +#include + + +#define luaL_newlibtable(L, l) \ + (lua_createtable((L), 0, sizeof((l)) / sizeof(*(l)) - 1)) + +#define luaL_newlib(L, l) \ + (luaL_newlibtable((L), (l)), luaL_register((L), NULL, (l))) + +/* debug functions */ + +void lua_stack_dump(lua_State *L); +void lua_table_dump(lua_State *L, const char *prefix); + +/* aux functions */ + +int lua_traceback(lua_State *L); +int luaL_loadbuffer_wrap(lua_State *L, const char *buff, + size_t sz, const char *name); +int lua_pcall_wrap(lua_State *L, int nargs, int nresults, int errfunc); + +void luaL_requiref(lua_State *L, const char *modname, + lua_CFunction openf, int glb); + +void settopfenvfrom(lua_State *L, int from); + +struct cflag_opt { + const char *name; + unsigned int flag; +}; + +unsigned int tocflags(lua_State *L, int idx, int top, + const struct cflag_opt *opts, unsigned int d); + +const char * +fromcflags(const struct cflag_opt *opts, unsigned int flag, const char *d); + +void table_fromopts(lua_State *L, const struct cflag_opt *opts, + unsigned int bitfield, unsigned int mask); + +void **newcptr(lua_State *L, const char *metatable); +void createmeta(lua_State *L, const char *name, + const luaL_Reg *meth, int index, int pop); +void *checkudata(lua_State *L, int ud, const char *name); + +struct const_value { + const char *name; + intptr_t value; +}; + +#define CONST_DEFINE(name) { #name, name } + +void setconst(lua_State *L, const struct const_value *cv); + +#endif /* ! _SECURITY_LUA_LSM_AUXLIB_H */ diff --git a/security/lua/debug.h b/security/lua/debug.h new file mode 100644 index 000000000000..768d3c18e648 --- /dev/null +++ b/security/lua/debug.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Lua based LSM + * + * Copyright (C) 2025 The Alibaba Cloud Linux Authors. + */ + +#ifndef _SECURITY_LUA_LSM_DEBUG_H +#define _SECURITY_LUA_LSM_DEBUG_H + +#ifdef DEBUG + +#include +#include +#include + +extern bool debug; + +#define __log_level(level, fmt, ...) \ + do { \ + if (debug) \ + pr_ ## level("%s [%d]: %d %s - " fmt, \ + current->comm, task_pid_vnr(current), \ + __LINE__, __func__, ##__VA_ARGS__); \ + } while (0) + +#define __log_emerg(fmt, ...) __log_level(emerg, fmt, ##__VA_ARGS__) +#define __log_err(fmt, ...) __log_level(err, fmt, ##__VA_ARGS__) +#define __log_warn(fmt, ...) __log_level(warn, fmt, ##__VA_ARGS__) +#define __log_info(fmt, ...) __log_level(info, fmt, ##__VA_ARGS__) +#define __log_info_ratelimited(fmt, ...) \ + __log_level(info_ratelimited, fmt, ##__VA_ARGS__) + +#else + +#define __log_emerg(fmt, ...) do {} while (0) +#define __log_err(fmt, ...) do {} while (0) +#define __log_warn(fmt, ...) do {} while (0) +#define __log_info(fmt, ...) do {} while (0) +#define __log_info_ratelimited(fmt, ...) do {} while (0) + +#endif + +#endif /* ! _SECURITY_LUA_LSM_DEBUG_H */ From ebc332ff3f5e53c52bf0b168d8889b733c67f686 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Fri, 22 Aug 2025 12:49:01 +0800 Subject: [PATCH 017/110] lua-lsm: add type definition header files Signed-off-by: Tianjia Zhang Co-developed-by: Zongyao Chen --- security/lua/kvcache.h | 88 ++++++++++++++ security/lua/lsm.h | 197 +++++++++++++++++++++++++++++++ security/lua/lsm_defs.h | 242 ++++++++++++++++++++++++++++++++++++++ security/lua/lua_object.h | 130 ++++++++++++++++++++ 4 files changed, 657 insertions(+) create mode 100644 security/lua/kvcache.h create mode 100644 security/lua/lsm.h create mode 100644 security/lua/lsm_defs.h create mode 100644 security/lua/lua_object.h diff --git a/security/lua/kvcache.h b/security/lua/kvcache.h new file mode 100644 index 000000000000..0063f992be84 --- /dev/null +++ b/security/lua/kvcache.h @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Lua based LSM + * + * Copyright (C) 2025 The Alibaba Cloud Linux Authors. + */ + +#ifndef _SECURITY_LUA_LSM_KVCACHE_H +#define _SECURITY_LUA_LSM_KVCACHE_H + +#include +#include +#undef LIST_HEAD +#include "queue.h" +#undef RB_ROOT +#include "tree.h" + +#define CACHE_CAPACITY 1024 + +struct lua_module; + +struct kvcache_node { + const char *key; + struct lua_module *module; + struct kvcache_dict *dict; + int tt; + union { + int b; + lua_Number n; + void *p; + struct { + const char *s; + size_t l; + } s; + struct { + TAILQ_HEAD(, kvcache_node) h; + size_t l; + } q; + }; + union { + struct { + RB_ENTRY(kvcache_node) node; + TAILQ_ENTRY(kvcache_node) modlist; + }; + TAILQ_ENTRY(kvcache_node) qlist; + }; +}; + +struct kvcache_dict { + int capacity; + atomic_t count; + rwlock_t lock; + RB_HEAD(kvcache, kvcache_node) root; +}; + +void kvcache_module_nodes_gc(struct lua_module *module); +void kvcache_dict_free(struct kvcache_dict *dict); +void kvcache_dict_init(struct kvcache_dict *dict); +void kvcache_status(int *nalloc, int *nfree); + +/******************************** object cache *******************************/ + +extern const int _module_sentinel; +#define MODULE_KEY ((void *)&_module_sentinel) + +int lua_object_get(lua_State *L, struct kvcache_dict *dict); +int lua_object_incr(lua_State *L, struct kvcache_dict *dict); +int lua_object_lpush(lua_State *L, struct kvcache_dict *dict); +int lua_object_rpush(lua_State *L, struct kvcache_dict *dict); +int lua_object_lpop(lua_State *L, struct kvcache_dict *dict); +int lua_object_rpop(lua_State *L, struct kvcache_dict *dict); +int lua_object_llen(lua_State *L, struct kvcache_dict *dict); +int lua_object_index(lua_State *L, struct kvcache_dict *dict); +int lua_object_newindex(lua_State *L, struct kvcache_dict *dict); + +/******************************** shared dict ********************************/ + +#define METH_SHARED_DICT "meth_shared_dict" + +#define newshdict(L) \ + ((struct kvcache_dict **)newcptr((L), METH_SHARED_DICT)) +#define toshdictp(L, idx) \ + ((struct kvcache_dict **)luaL_checkudata((L), (idx), METH_SHARED_DICT)) +#define toshdict(L, idx) (*toshdictp(L, idx)) + +int shdict_init(lua_State *L); + +#endif /* ! _SECURITY_LUA_LSM_KVCACHE_H */ diff --git a/security/lua/lsm.h b/security/lua/lsm.h new file mode 100644 index 000000000000..d66937406ef2 --- /dev/null +++ b/security/lua/lsm.h @@ -0,0 +1,197 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Lua based LSM + * + * Copyright (C) 2025 The Alibaba Cloud Linux Authors. + */ + +#ifndef _SECURITY_LUA_LSM_LSM_H +#define _SECURITY_LUA_LSM_LSM_H + +#include +#include +#include +#include +#include +#include +#include +#include "bitmap.h" +#undef LIST_HEAD +#include "queue.h" +#include "kvcache.h" + + +#define LUA_LSM_VERSION 1 + + +struct lua_lsm_hook_stat { + const char *name; + atomic_t nhooks; + TAILQ_ENTRY(lua_lsm_hook_stat) list; +#ifdef CONFIG_SECURITY_LUA_LSM_STATS + atomic_t count; + atomic64_t time; /* ns */ + atomic64_t maxtime; +#endif +}; + +extern struct lua_lsm_hook_stat lua_lsm_hook_stats[]; + + +#define LSM_HOOK(RET, DEFAULT, NAME, ...) \ + int __prepare_ ## NAME(__VA_ARGS__); \ + void __postpone_ ## NAME(__VA_ARGS__); + +#include +#undef LSM_HOOK + + +enum { + #define LSM_HOOK(RET, DEFAULT, NAME, ...) __LL_NR_ ## NAME, + #include + #undef LSM_HOOK + __LL_NR_MAX +}; + +struct lua_module_shdict { + TAILQ_ENTRY(lua_module_shdict) list; + const char *name; + struct kvcache_dict dict; +}; + +struct lua_module { + const char *name; + const char *author; + const char *description; + const char *license; + int version; + TAILQ_ENTRY(lua_module) list; + __BITMAP_TYPE(, uint32_t, __LL_NR_MAX) hookfuncs; + int nhooks; + char *chunk; + size_t chunk_len; + TAILQ_HEAD(, lua_module_shdict) shdict; + rwlock_t shdict_lock; + atomic_t shdict_count; + TAILQ_HEAD(, kvcache_node) kvnodes; + spinlock_t kvnodes_lock; +}; + +TAILQ_HEAD(lua_modules_head, lua_module); + +extern struct lua_modules_head lsm_modules; +extern rwlock_t modules_lock; + + +#define TABLINE "--------------------------------------------" \ + "--------------------------------------------" + +int lua_module_register(const char *code, size_t len); +int lua_module_unregister(const char *name); + +int modules_show(struct seq_file *m, void *v); + +#ifdef CONFIG_SECURITY_LUA_LSM_STATS +int lua_lsm_status_show(struct seq_file *m, void *v); +int lsmhook_stat_show(struct seq_file *m, void *v); +#endif + + +extern struct lsm_blob_sizes lua_lsm_blob_sizes; + +struct lua_lsm_task { + lua_State *L; + struct kvcache_dict dict; +}; + +static inline struct lua_lsm_task *lua_lsm_task(const struct task_struct *task) +{ + return task->security + lua_lsm_blob_sizes.lbs_task; +} + +/* common object */ + +struct lua_lsm_object { + struct kvcache_dict dict; +}; + +static inline struct lua_lsm_object *lua_lsm_cred(const struct cred *cred) +{ + return cred->security + lua_lsm_blob_sizes.lbs_cred; +} + +static inline struct lua_lsm_object *lua_lsm_file(const struct file *file) +{ + return file->f_security + lua_lsm_blob_sizes.lbs_file; +} + +static inline struct lua_lsm_object *lua_lsm_ib(void *ib_sec) +{ + return ib_sec + lua_lsm_blob_sizes.lbs_ib; +} + +static inline struct lua_lsm_object *lua_lsm_inode(const struct inode *inode) +{ + if (unlikely(!inode->i_security)) + return NULL; + return inode->i_security + lua_lsm_blob_sizes.lbs_inode; +} + +static inline struct lua_lsm_object *lua_lsm_inode_rcu(void *inode_security) +{ + return inode_security + lua_lsm_blob_sizes.lbs_inode; +} + +static inline struct lua_lsm_object *lua_lsm_sock(const struct sock *sock) +{ + return sock->sk_security + lua_lsm_blob_sizes.lbs_sock; +} + +static inline struct lua_lsm_object *lua_lsm_superblock(const struct super_block *superblock) +{ + return superblock->s_security + lua_lsm_blob_sizes.lbs_superblock; +} + +static inline struct lua_lsm_object *lua_lsm_ipc(const struct kern_ipc_perm *ipc) +{ + return ipc->security + lua_lsm_blob_sizes.lbs_ipc; +} + +static inline struct lua_lsm_object *lua_lsm_key(const struct key *key) +{ + return key->security + lua_lsm_blob_sizes.lbs_key; +} + +static inline struct lua_lsm_object *lua_lsm_msgmsg(const struct msg_msg *msg) +{ + return msg->security + lua_lsm_blob_sizes.lbs_msg_msg; +} + +static inline struct lua_lsm_object *lua_lsm_perfevent(void *perf_event) +{ + return perf_event + lua_lsm_blob_sizes.lbs_perf_event; +} + +static inline struct lua_lsm_object *lua_lsm_tun_dev(void *security) +{ + return security + lua_lsm_blob_sizes.lbs_tun_dev; +} + +static inline struct lua_lsm_object *lua_lsm_bdev(const struct block_device *bdev) +{ + return bdev->bd_security + lua_lsm_blob_sizes.lbs_bdev; +} + +int lua_task_blob_init(struct task_struct *task); +void lua_task_blob_free(struct task_struct *task); + +/* lua C module */ + +int luaopen_kernel(lua_State *L); +int luaopen_fs(lua_State *L); +int luaopen_net(lua_State *L); +int luaopen_errno(lua_State *L); +int luaopen_capability(lua_State *L); +int luaopen_signal(lua_State *L); + +#endif /* ! _SECURITY_LUA_LSM_LSM_H */ diff --git a/security/lua/lsm_defs.h b/security/lua/lsm_defs.h new file mode 100644 index 000000000000..66f19ee9f9a1 --- /dev/null +++ b/security/lua/lsm_defs.h @@ -0,0 +1,242 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Lua based LSM + * + * Copyright (C) 2025 The Alibaba Cloud Linux Authors. + */ + +#ifndef _SECURITY_LUA_LSM_LSM_DEFS_H +#define _SECURITY_LUA_LSM_LSM_DEFS_H + +#include "lsm.h" + + +#define LSM_RET_DEFAULT(NAME) (NAME##_default) + +#define LSM_HOOK(RET, DEFAULT, NAME, ...) \ + extern const int __maybe_unused LSM_RET_DEFAULT(NAME); +#include +#undef LSM_HOOK + + +#ifdef CONFIG_SECURITY_LUA_LSM_STATS + +#define START_STATS(NAME) \ + do { \ + ktime_t ___start = ktime_get(); \ + s64 ___delta; \ + atomic_inc(&lua_lsm_hook_stats[__LL_NR_ ## NAME].count); + +#define END_STATS(NAME) \ + ___delta = ktime_to_ns(ktime_get()) - ktime_to_ns(___start); \ + if (atomic64_read(&lua_lsm_hook_stats[__LL_NR_ ## NAME].maxtime) < ___delta) \ + atomic64_set(&lua_lsm_hook_stats[__LL_NR_ ## NAME].maxtime, ___delta); \ + atomic64_add(___delta, &lua_lsm_hook_stats[__LL_NR_ ## NAME].time); \ + } while (0) + +#else + +#define START_STATS(NAME) +#define END_STATS(NAME) + +#endif + + +#define DECL_ARGS_0 void +#define DECL_ARGS_1 +#define DECL_ARGS_2 +#define DECL_ARGS_3 +#define DECL_ARGS_4 +#define DECL_ARGS_5 +#define DECL_ARGS_6 + +#define ASSIGN_FROM_FUNC_void(ret) +#define ASSIGN_FROM_FUNC_int(ret) ret = + +#define PCALL_RES_void(L, top, ret) do {} while (0) + +/* + * hooks result format: + * [none] : default value + * nil : default value + * true : 0 + * false : -EPERM + * false, errno : -errno + * nil, errno : -errno + */ +#define PCALL_RES_int(L, top, ret) \ + do { \ + /* stack: [..., _M, res1, ...] */ \ + int nres = lua_gettop(L) - top + 1; \ + switch (nres) { \ + case 0: \ + break; \ + case 1: \ + if (lua_type(L, top) == LUA_TBOOLEAN) \ + ret = lua_toboolean(L, top) ? 0 : -EPERM; \ + break; \ + default: \ + if (!lua_toboolean(L, top)) { \ + int errno = lua_tointeger(L, top + 1); \ + if (errno > 0 && errno <= MAX_ERRNO) \ + ret = -errno; \ + } \ + break; \ + } \ + lua_pop(L, nres); \ + } while (0) + +#define LCALL_NRES_void 0 +#define LCALL_NRES_int LUA_MULTRET + +#define LUA_PCALL(rettype, L, top, ret) \ + do { \ + int status, nargs = lua_gettop(L) - top; \ + lua_pushvalue(L, -nargs - 4); /* env */ \ + lua_setfenv(L, -nargs - 6); /* restore thread.env */ \ + status = lua_pcall(L, nargs, LCALL_NRES_ ## rettype, -nargs - 6); \ + if (status != 0) { \ + const char * __maybe_unused error = lua_tostring(L, -1); \ + __log_err("pcall: status = %d, top = %d, %s\n", \ + status, lua_gettop(L), error); \ + lua_pop(L, 1); \ + } else { \ + PCALL_RES_ ## rettype(L, top, ret); \ + } \ + } while (0) + + +#define END_VM_CALL_void(rettype, L, top, ret) LUA_PCALL(rettype, L, top, ret) +#define END_VM_CALL_int(rettype, L, top, ret) lua_settop(L, (top) - 1) + +#define RET_CHECK_void(NAME, ret) do {} while (0) + +#define RET_CHECK_int(NAME, ret) \ + if ((ret) && (ret) != LSM_RET_DEFAULT(NAME)) \ + break + + +#define LUA_LSM_DEFINEx(x, NAME, rettype, vmtype, ...) \ + static inline vmtype __lua_lsm_vm_ ## NAME(lua_State *L __VA_OPT__(,) \ + __MAP(x, __SC_DECL, __VA_ARGS__)); \ + static inline int __lua_lsm_ ## NAME(DECL_ARGS_ ## x \ + __MAP(x, __SC_DECL, __VA_ARGS__)) \ + { \ + lua_State *L = lua_lsm_task(current)->L; \ + struct lua_module *module; \ + int ret = LSM_RET_DEFAULT(NAME); \ + if (atomic_read(&lua_lsm_hook_stats[__LL_NR_ ## NAME].nhooks) == 0) \ + return ret; \ + lua_pushcfunction(L, lua_traceback); \ + lua_pushthread(L); \ + lua_getfenv(L, -1); /* save thread.fenv */ \ + lua_getfield(L, LUA_REGISTRYINDEX, "_MODULES"); \ + /* stack: [traceback, thread, env, _MODULES] */ \ + TAILQ_FOREACH(module, &lsm_modules, list) { \ + if (!__BITMAP_ISSET(__LL_NR_ ## NAME, &module->hookfuncs)) \ + continue; \ + lua_getfield(L, -1, module->name); \ + lua_getfield(L, -1, #NAME); \ + /* stack: [traceback, thread, env, _MODULES, _M, lfunc] */ \ + if (lua_isfunction(L, -1)) { \ + int top = lua_gettop(L); \ + lua_getfenv(L, -1); \ + lua_setfenv(L, -6); /* thread.fenv = lfunc.fenv */ \ + ASSIGN_FROM_FUNC_ ## vmtype(ret) \ + __lua_lsm_vm_ ## NAME(L __VA_OPT__(,) \ + __MAP(x, __SC_ARGS, __VA_ARGS__)); \ + END_VM_CALL_ ## vmtype(rettype, L, top, ret); \ + } else { \ + lua_pop(L, 1); /* pop lfunc */ \ + } \ + lua_pop(L, 1); /* pop _M */ \ + RET_CHECK_ ## rettype(NAME, ret); \ + } \ + lua_pop(L, 4); \ + return ret; \ + } \ + rettype lua_lsm_ ## NAME(DECL_ARGS_ ## x \ + __MAP(x, __SC_DECL, __VA_ARGS__)) \ + { \ + int ret; \ + START_STATS(NAME); \ + read_lock_bh(&modules_lock); \ + ret = __prepare_ ## NAME(__MAP(x, __SC_ARGS, __VA_ARGS__)); \ + if (ret >= 0) { \ + ret = __lua_lsm_ ## NAME(__MAP(x, __SC_ARGS, __VA_ARGS__)); \ + __postpone_ ## NAME(__MAP(x, __SC_ARGS, __VA_ARGS__)); \ + } \ + read_unlock_bh(&modules_lock); \ + END_STATS(NAME); \ + return (rettype)ret; \ + } \ + static inline vmtype __lua_lsm_vm_ ## NAME(lua_State *L __VA_OPT__(,) \ + __MAP(x, __SC_DECL, __VA_ARGS__)) + +/*****************************************************************************/ + +#define LUA_LSM_VOID_DEFINE0(name, ...) LUA_LSM_DEFINEx(0, name, void, void, ##__VA_ARGS__) +#define LUA_LSM_VOID_DEFINE1(name, ...) LUA_LSM_DEFINEx(1, name, void, void, ##__VA_ARGS__) +#define LUA_LSM_VOID_DEFINE2(name, ...) LUA_LSM_DEFINEx(2, name, void, void, ##__VA_ARGS__) +#define LUA_LSM_VOID_DEFINE3(name, ...) LUA_LSM_DEFINEx(3, name, void, void, ##__VA_ARGS__) +#define LUA_LSM_VOID_DEFINE4(name, ...) LUA_LSM_DEFINEx(4, name, void, void, ##__VA_ARGS__) +#define LUA_LSM_VOID_DEFINE5(name, ...) LUA_LSM_DEFINEx(5, name, void, void, ##__VA_ARGS__) +#define LUA_LSM_VOID_DEFINE6(name, ...) LUA_LSM_DEFINEx(6, name, void, void, ##__VA_ARGS__) + +#define LUA_LSM_VOID_NAKED_DEFINE0(name, ...) LUA_LSM_DEFINEx(0, name, void, int, ##__VA_ARGS__) +#define LUA_LSM_VOID_NAKED_DEFINE1(name, ...) LUA_LSM_DEFINEx(1, name, void, int, ##__VA_ARGS__) +#define LUA_LSM_VOID_NAKED_DEFINE2(name, ...) LUA_LSM_DEFINEx(2, name, void, int, ##__VA_ARGS__) +#define LUA_LSM_VOID_NAKED_DEFINE3(name, ...) LUA_LSM_DEFINEx(3, name, void, int, ##__VA_ARGS__) +#define LUA_LSM_VOID_NAKED_DEFINE4(name, ...) LUA_LSM_DEFINEx(4, name, void, int, ##__VA_ARGS__) +#define LUA_LSM_VOID_NAKED_DEFINE5(name, ...) LUA_LSM_DEFINEx(5, name, void, int, ##__VA_ARGS__) +#define LUA_LSM_VOID_NAKED_DEFINE6(name, ...) LUA_LSM_DEFINEx(6, name, void, int, ##__VA_ARGS__) + +#define LUA_LSM_INT_DEFINE0(name, ...) LUA_LSM_DEFINEx(0, name, int, void, ##__VA_ARGS__) +#define LUA_LSM_INT_DEFINE1(name, ...) LUA_LSM_DEFINEx(1, name, int, void, ##__VA_ARGS__) +#define LUA_LSM_INT_DEFINE2(name, ...) LUA_LSM_DEFINEx(2, name, int, void, ##__VA_ARGS__) +#define LUA_LSM_INT_DEFINE3(name, ...) LUA_LSM_DEFINEx(3, name, int, void, ##__VA_ARGS__) +#define LUA_LSM_INT_DEFINE4(name, ...) LUA_LSM_DEFINEx(4, name, int, void, ##__VA_ARGS__) +#define LUA_LSM_INT_DEFINE5(name, ...) LUA_LSM_DEFINEx(5, name, int, void, ##__VA_ARGS__) +#define LUA_LSM_INT_DEFINE6(name, ...) LUA_LSM_DEFINEx(6, name, int, void, ##__VA_ARGS__) + +#define LUA_LSM_INT_NAKED_DEFINE0(name, ...) LUA_LSM_DEFINEx(0, name, int, int, ##__VA_ARGS__) +#define LUA_LSM_INT_NAKED_DEFINE1(name, ...) LUA_LSM_DEFINEx(1, name, int, int, ##__VA_ARGS__) +#define LUA_LSM_INT_NAKED_DEFINE2(name, ...) LUA_LSM_DEFINEx(2, name, int, int, ##__VA_ARGS__) +#define LUA_LSM_INT_NAKED_DEFINE3(name, ...) LUA_LSM_DEFINEx(3, name, int, int, ##__VA_ARGS__) +#define LUA_LSM_INT_NAKED_DEFINE4(name, ...) LUA_LSM_DEFINEx(4, name, int, int, ##__VA_ARGS__) +#define LUA_LSM_INT_NAKED_DEFINE5(name, ...) LUA_LSM_DEFINEx(5, name, int, int, ##__VA_ARGS__) +#define LUA_LSM_INT_NAKED_DEFINE6(name, ...) LUA_LSM_DEFINEx(6, name, int, int, ##__VA_ARGS__) + + +/* prepare and postpone function macro */ + +#define LUA_LSM_PREPARE_DEFINEx(x, NAME, ...) \ + inline int __prepare_ ## NAME(DECL_ARGS_ ## x \ + __MAP(x, __SC_DECL, __VA_ARGS__)) + +#define LUA_LSM_POSTPONE_DEFINEx(x, NAME, ...) \ + inline void __postpone_ ## NAME(DECL_ARGS_ ## x \ + __MAP(x, __SC_DECL, __VA_ARGS__)) + +#define LUA_LSM_PREPARE_DEFINE0(NAME, ...) LUA_LSM_PREPARE_DEFINEx(0, NAME, ##__VA_ARGS__) +#define LUA_LSM_PREPARE_DEFINE1(NAME, ...) LUA_LSM_PREPARE_DEFINEx(1, NAME, ##__VA_ARGS__) +#define LUA_LSM_PREPARE_DEFINE2(NAME, ...) LUA_LSM_PREPARE_DEFINEx(2, NAME, ##__VA_ARGS__) +#define LUA_LSM_PREPARE_DEFINE3(NAME, ...) LUA_LSM_PREPARE_DEFINEx(3, NAME, ##__VA_ARGS__) +#define LUA_LSM_PREPARE_DEFINE4(NAME, ...) LUA_LSM_PREPARE_DEFINEx(4, NAME, ##__VA_ARGS__) +#define LUA_LSM_PREPARE_DEFINE5(NAME, ...) LUA_LSM_PREPARE_DEFINEx(5, NAME, ##__VA_ARGS__) +#define LUA_LSM_PREPARE_DEFINE6(NAME, ...) LUA_LSM_PREPARE_DEFINEx(6, NAME, ##__VA_ARGS__) + +#define LUA_LSM_POSTPONE_DEFINE0(NAME, ...) LUA_LSM_POSTPONE_DEFINEx(0, NAME, ##__VA_ARGS__) +#define LUA_LSM_POSTPONE_DEFINE1(NAME, ...) LUA_LSM_POSTPONE_DEFINEx(1, NAME, ##__VA_ARGS__) +#define LUA_LSM_POSTPONE_DEFINE2(NAME, ...) LUA_LSM_POSTPONE_DEFINEx(2, NAME, ##__VA_ARGS__) +#define LUA_LSM_POSTPONE_DEFINE3(NAME, ...) LUA_LSM_POSTPONE_DEFINEx(3, NAME, ##__VA_ARGS__) +#define LUA_LSM_POSTPONE_DEFINE4(NAME, ...) LUA_LSM_POSTPONE_DEFINEx(4, NAME, ##__VA_ARGS__) +#define LUA_LSM_POSTPONE_DEFINE5(NAME, ...) LUA_LSM_POSTPONE_DEFINEx(5, NAME, ##__VA_ARGS__) +#define LUA_LSM_POSTPONE_DEFINE6(NAME, ...) LUA_LSM_POSTPONE_DEFINEx(6, NAME, ##__VA_ARGS__) + + +#define LSM_HOOK(RET, DEFAULT, NAME, ...) RET lua_lsm_ ## NAME(__VA_ARGS__); +#include +#undef LSM_HOOK + +#endif /* ! _SECURITY_LUA_LSM_LSM_DEFS_H */ diff --git a/security/lua/lua_object.h b/security/lua/lua_object.h new file mode 100644 index 000000000000..838fcb89fb99 --- /dev/null +++ b/security/lua/lua_object.h @@ -0,0 +1,130 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Lua based LSM + * + * Copyright (C) 2025 The Alibaba Cloud Linux Authors. + */ + +#ifndef _SECURITY_LUA_LSM_LUA_OBJECT_H +#define _SECURITY_LUA_LSM_LUA_OBJECT_H + +#include "auxlib.h" + +#define METHOD_NAME(class, name) ("method." #class "." #name) + +#define LUA_OBJECT_DEFINE(class, name, ctype, d) \ + static inline ctype *new ## name(lua_State *L) \ + { \ + ctype *p = (ctype *)lua_newuserdata(L, sizeof(ctype)); \ + *p = d; \ + luaL_getmetatable(L, METHOD_NAME(class, name)); \ + lua_setmetatable(L, -2); \ + return p; \ + } \ + static inline ctype *to ## name ## p(lua_State *L, int idx) \ + { \ + return (ctype *)luaL_checkudata(L, idx, METHOD_NAME(class, name)); \ + } \ + static inline ctype to ## name(lua_State *L, int idx) \ + { \ + return *to ## name ## p(L, idx); \ + } \ + static inline ctype *checkudata_ ## name(lua_State *L, int idx) \ + { \ + return (ctype *)checkudata(L, idx, METHOD_NAME(class, name)); \ + } \ + static int class ## _ ## name ## _tostring(lua_State *L) \ + { \ + ctype o = to ## name(L, 1); \ + lua_pushfstring(L, #name " (%p)", o); \ + return 1; \ + } + +#define LUA_OBJECT_KVCACHE_FUNC(class, name, ctype, blob, method, fname) \ + static int class ## _ ## name ## _ ## method(lua_State *L) \ + { \ + ctype p = to ## name(L, 1); \ + struct lua_lsm_ ## blob *ll = lua_lsm_ ## name(p); \ + return lua_object_ ## fname(L, &ll->dict); \ + } + +#define LUA_OBJECT_BLOB_FUNCS_DEFINE(class, name, ctype, d, blob) \ + LUA_OBJECT_DEFINE(class, name, ctype, d) \ + LUA_OBJECT_KVCACHE_FUNC(class, name, ctype, blob, kvcache_get, get) \ + LUA_OBJECT_KVCACHE_FUNC(class, name, ctype, blob, kvcache_incr, incr) \ + LUA_OBJECT_KVCACHE_FUNC(class, name, ctype, blob, kvcache_lpush, lpush) \ + LUA_OBJECT_KVCACHE_FUNC(class, name, ctype, blob, kvcache_rpush, rpush) \ + LUA_OBJECT_KVCACHE_FUNC(class, name, ctype, blob, kvcache_lpop, lpop) \ + LUA_OBJECT_KVCACHE_FUNC(class, name, ctype, blob, kvcache_rpop, rpop) \ + LUA_OBJECT_KVCACHE_FUNC(class, name, ctype, blob, kvcache_llen, llen) \ + LUA_OBJECT_KVCACHE_FUNC(class, name, ctype, blob, index, index) \ + LUA_OBJECT_KVCACHE_FUNC(class, name, ctype, blob, newindex, newindex) \ + static inline void create_ ## name ## _meta(lua_State *L, \ + const luaL_Reg *funcs) \ + { \ + static const luaL_Reg object_meth[] = { \ + { "kvcache_set", class ## _ ## name ## _newindex }, \ + { "kvcache_get", class ## _ ## name ## _kvcache_get }, \ + { "kvcache_incr", class ## _ ## name ## _kvcache_incr }, \ + { "kvcache_lpush", class ## _ ## name ## _kvcache_lpush }, \ + { "kvcache_rpush", class ## _ ## name ## _kvcache_rpush }, \ + { "kvcache_lpop", class ## _ ## name ## _kvcache_lpop }, \ + { "kvcache_rpop", class ## _ ## name ## _kvcache_rpop }, \ + { "kvcache_llen", class ## _ ## name ## _kvcache_llen }, \ + { "__index", class ## _ ## name ## _index }, \ + { "__newindex", class ## _ ## name ## _newindex }, \ + { "__tostring", class ## _ ## name ## _tostring }, \ + { NULL, NULL } \ + }; \ + createmeta(L, METHOD_NAME(class, name), funcs, 0, 0); \ + luaL_register(L, NULL, object_meth); \ + lua_pop(L, 1); \ + } + +#define LUA_OBJECT_func_DEFINE(class, name, ctype, d) \ + LUA_OBJECT_DEFINE(class, name, ctype, d) \ + static inline void create_ ## name ## _meta(lua_State *L, \ + const luaL_Reg *funcs) \ + { \ + createmeta(L, METHOD_NAME(class, name), funcs, 1, 0); \ + lua_pushcfunction(L, class ## _ ## name ## _tostring); \ + lua_setfield(L, -2, "__tostring"); /* mt.__tostring = func */ \ + lua_pop(L, 1); \ + } + +#define LUA_OBJECT_task_DEFINE(class, name, ctype, d) \ + LUA_OBJECT_BLOB_FUNCS_DEFINE(class, name, ctype, d, task) +#define LUA_OBJECT_object_DEFINE(class, name, ctype, d) \ + LUA_OBJECT_BLOB_FUNCS_DEFINE(class, name, ctype, d, object) + + +#define LUA_OBJECTS_LIST \ + LUA_OBJECT(task, kernel, task, struct task_struct *, NULL) \ + LUA_OBJECT(object, kernel, cred, struct cred *, NULL) \ + LUA_OBJECT(object, kernel, perfevent, struct perf_event *, NULL) \ + LUA_OBJECT(object, ipc, ipc, struct kern_ipc_perm *, NULL) \ + LUA_OBJECT(object, ipc, msgmsg, struct msg_msg *, NULL) \ + LUA_OBJECT(object, net, sock, struct sock *, NULL) \ + LUA_OBJECT(func, net, socket, struct socket *, NULL) \ + LUA_OBJECT(func, net, skb, struct sk_buff *, NULL) \ + LUA_OBJECT(func, net, sockaddr, struct sockaddr *, NULL) \ + LUA_OBJECT(object, security, key, struct key *, NULL) \ + LUA_OBJECT(object, block, bdev, struct block_device *, NULL) \ + LUA_OBJECT(object, fs, inode, struct inode *, NULL) \ + LUA_OBJECT(object, fs, file, struct file *, NULL) \ + LUA_OBJECT(object, fs, superblock, struct super_block *, NULL) \ + LUA_OBJECT(func, fs, dentry, struct dentry *, NULL) \ + LUA_OBJECT(func, fs, binprm, struct linux_binprm *, NULL) \ + LUA_OBJECT(func, fs, path, struct path *, NULL) \ + LUA_OBJECT(func, fs, fscontext, struct fs_context *, NULL) \ + LUA_OBJECT(func, fs, vfsmount, struct vfsmount *, NULL) \ + LUA_OBJECT(func, fs, mntidmap, struct mnt_idmap *, NULL) \ + LUA_OBJECT(func, kernel, cap, kernel_cap_t, CAP_EMPTY_SET) + + +#define LUA_OBJECT(blob, class, name, ctype, d) \ + LUA_OBJECT_ ## blob ## _DEFINE(class, name, ctype, d) +LUA_OBJECTS_LIST +#undef LUA_OBJECT + +#endif /* ! _SECURITY_LUA_LSM_LUA_OBJECT_H */ From 9a34db7783b463ae9f9e3f265400585a6cac02ee Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Fri, 22 Aug 2025 12:49:53 +0800 Subject: [PATCH 018/110] lua-lsm: api - add commonly used constant definitions Signed-off-by: Tianjia Zhang --- security/lua/lua_capability.c | 174 ++++++++++++++++++++++++++++++++++ security/lua/lua_errno.c | 69 ++++++++++++++ security/lua/lua_signal.c | 55 +++++++++++ 3 files changed, 298 insertions(+) create mode 100644 security/lua/lua_capability.c create mode 100644 security/lua/lua_errno.c create mode 100644 security/lua/lua_signal.c diff --git a/security/lua/lua_capability.c b/security/lua/lua_capability.c new file mode 100644 index 000000000000..bfe783769588 --- /dev/null +++ b/security/lua/lua_capability.c @@ -0,0 +1,174 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Lua based LSM + * + * Copyright (C) 2025 The Alibaba Cloud Linux Authors. + */ + +#include "debug.h" +#include +#include "lsm.h" +#include "auxlib.h" +#include "lua_object.h" + +static const struct const_value capabilities[] = { + CONST_DEFINE(CAP_CHOWN), + CONST_DEFINE(CAP_DAC_OVERRIDE), + CONST_DEFINE(CAP_DAC_READ_SEARCH), + CONST_DEFINE(CAP_FOWNER), + CONST_DEFINE(CAP_FSETID), + CONST_DEFINE(CAP_KILL), + CONST_DEFINE(CAP_SETGID), + CONST_DEFINE(CAP_SETUID), + CONST_DEFINE(CAP_SETPCAP), + CONST_DEFINE(CAP_LINUX_IMMUTABLE), + CONST_DEFINE(CAP_NET_BIND_SERVICE), + CONST_DEFINE(CAP_NET_BROADCAST), + CONST_DEFINE(CAP_NET_ADMIN), + CONST_DEFINE(CAP_NET_RAW), + CONST_DEFINE(CAP_IPC_LOCK), + CONST_DEFINE(CAP_IPC_OWNER), + CONST_DEFINE(CAP_SYS_MODULE), + CONST_DEFINE(CAP_SYS_RAWIO), + CONST_DEFINE(CAP_SYS_CHROOT), + CONST_DEFINE(CAP_SYS_PTRACE), + CONST_DEFINE(CAP_SYS_PACCT), + CONST_DEFINE(CAP_SYS_ADMIN), + CONST_DEFINE(CAP_SYS_BOOT), + CONST_DEFINE(CAP_SYS_NICE), + CONST_DEFINE(CAP_SYS_RESOURCE), + CONST_DEFINE(CAP_SYS_TIME), + CONST_DEFINE(CAP_SYS_TTY_CONFIG), + CONST_DEFINE(CAP_MKNOD), + CONST_DEFINE(CAP_LEASE), + CONST_DEFINE(CAP_AUDIT_WRITE), + CONST_DEFINE(CAP_AUDIT_CONTROL), + CONST_DEFINE(CAP_SETFCAP), + CONST_DEFINE(CAP_MAC_OVERRIDE), + CONST_DEFINE(CAP_MAC_ADMIN), + CONST_DEFINE(CAP_SYSLOG), + CONST_DEFINE(CAP_WAKE_ALARM), + CONST_DEFINE(CAP_BLOCK_SUSPEND), + CONST_DEFINE(CAP_AUDIT_READ), + CONST_DEFINE(CAP_PERFMON), + CONST_DEFINE(CAP_BPF), + CONST_DEFINE(CAP_CHECKPOINT_RESTORE), + { NULL } +}; + +/********************************** meth **********************************/ + +static int capability_add(lua_State *L) +{ + kernel_cap_t cap1 = tocap(L, 1); + int tt = lua_type(L, 2); + if (tt == LUA_TUSERDATA) { + const kernel_cap_t cap2 = tocap(L, 2); + *newcap(L) = cap_combine(cap1, cap2); + return 1; + } else if (tt == LUA_TNUMBER) { + int flag = luaL_checkint(L, 2); + cap_raise(cap1, flag); + *newcap(L) = cap1; + return 1; + } else { + return 0; + } +} + +static int capability_sub(lua_State *L) +{ + kernel_cap_t cap1 = tocap(L, 1); + int tt = lua_type(L, 2); + if (tt == LUA_TUSERDATA) { + const kernel_cap_t cap2 = tocap(L, 2); + *newcap(L) = cap_drop(cap1, cap2); + return 1; + } else if (tt == LUA_TNUMBER) { + int flag = luaL_checkint(L, 2); + cap_lower(cap1, flag); + *newcap(L) = cap1; + return 1; + } else { + return 0; + } +} + +static int capability_mul(lua_State *L) +{ + const kernel_cap_t cap1 = tocap(L, 1); + const kernel_cap_t cap2 = tocap(L, 2); + *newcap(L) = cap_intersect(cap1, cap2); + return 1; +} + +static int capability_eq(lua_State *L) +{ + const kernel_cap_t cap1 = tocap(L, 1); + const kernel_cap_t cap2 = tocap(L, 2); + lua_pushboolean(L, cap_isidentical(cap1, cap2)); + return 1; +} + +static int capability_le(lua_State *L) +{ + const kernel_cap_t cap1 = tocap(L, 1); + const kernel_cap_t cap2 = tocap(L, 2); + lua_pushboolean(L, cap_issubset(cap1, cap2)); + return 1; +} + +static int capability_lt(lua_State *L) +{ + const kernel_cap_t cap1 = tocap(L, 1); + const kernel_cap_t cap2 = tocap(L, 2); + bool b = cap_issubset(cap1, cap2) && !cap_isidentical(cap1, cap2); + lua_pushboolean(L, b); + return 1; +} + +static const luaL_Reg cap_meth[] = { + { "__add", capability_add }, /* combine/raise */ + { "__sub", capability_sub }, /* drop/lower */ + { "__mul", capability_mul }, /* intersect */ + { "__eq", capability_eq }, /* isidentical */ + { "__le", capability_le }, /* issubset */ + { "__lt", capability_lt }, /* issubset && !isidentical */ + { NULL, NULL } +}; + +/********************************** lib **********************************/ + +static int capability_cap_empty(lua_State *L) +{ + *newcap(L) = CAP_EMPTY_SET; + return 1; +} + +static int capability_cap_full(lua_State *L) +{ + *newcap(L) = CAP_FULL_SET; + return 1; +} + +static int capability_capable(lua_State *L) +{ + int cap = luaL_checkinteger(L, 1); + lua_pushboolean(L, capable(cap)); + return 1; +} + +static const luaL_Reg capabilitylib[] = { + { "cap_empty", capability_cap_empty }, + { "cap_full", capability_cap_full }, + { "capable", capability_capable }, + { NULL, NULL } +}; + +int luaopen_capability(lua_State *L) +{ + luaL_newlib(L, capabilitylib); + create_cap_meta(L, cap_meth); + setconst(L, capabilities); + return 1; +} diff --git a/security/lua/lua_errno.c b/security/lua/lua_errno.c new file mode 100644 index 000000000000..8ca4b625ec51 --- /dev/null +++ b/security/lua/lua_errno.c @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Lua based LSM + * + * Copyright (C) 2025 The Alibaba Cloud Linux Authors. + */ + +#include "debug.h" +#include +#include +#include "lsm.h" +#include "auxlib.h" + +static const struct const_value errnos[] = { + CONST_DEFINE(EPERM), + CONST_DEFINE(ENOENT), + CONST_DEFINE(ESRCH), + CONST_DEFINE(EINTR), + CONST_DEFINE(EIO), + CONST_DEFINE(ENXIO), + CONST_DEFINE(E2BIG), + CONST_DEFINE(ENOEXEC), + CONST_DEFINE(EBADF), + CONST_DEFINE(ECHILD), + CONST_DEFINE(EAGAIN), + CONST_DEFINE(ENOMEM), + CONST_DEFINE(EACCES), + CONST_DEFINE(EFAULT), + CONST_DEFINE(ENOTBLK), + CONST_DEFINE(EBUSY), + CONST_DEFINE(EEXIST), + CONST_DEFINE(EXDEV), + CONST_DEFINE(ENODEV), + CONST_DEFINE(ENOTDIR), + CONST_DEFINE(EISDIR), + CONST_DEFINE(EINVAL), + CONST_DEFINE(ENFILE), + CONST_DEFINE(EMFILE), + CONST_DEFINE(ENOTTY), + CONST_DEFINE(ETXTBSY), + CONST_DEFINE(EFBIG), + CONST_DEFINE(ENOSPC), + CONST_DEFINE(ESPIPE), + CONST_DEFINE(EROFS), + CONST_DEFINE(EMLINK), + CONST_DEFINE(EPIPE), + CONST_DEFINE(EDOM), + CONST_DEFINE(ERANGE), + { NULL } +}; + +static int errno_errname(lua_State *L) +{ + int err = luaL_checkinteger(L, 1); + lua_pushstring(L, errname(err)); + return 1; +} + +static const luaL_Reg errnolib[] = { + { "errname", errno_errname }, + { NULL, NULL } +}; + +int luaopen_errno(lua_State *L) +{ + luaL_newlib(L, errnolib); + setconst(L, errnos); + return 1; +} diff --git a/security/lua/lua_signal.c b/security/lua/lua_signal.c new file mode 100644 index 000000000000..a721a5d3ae3a --- /dev/null +++ b/security/lua/lua_signal.c @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Lua based LSM + * + * Copyright (C) 2025 The Alibaba Cloud Linux Authors. + */ + +#include "debug.h" +#include +#include "lsm.h" +#include "auxlib.h" + +static const struct const_value signals[] = { + CONST_DEFINE(SIGHUP), + CONST_DEFINE(SIGINT), + CONST_DEFINE(SIGQUIT), + CONST_DEFINE(SIGILL), + CONST_DEFINE(SIGTRAP), + CONST_DEFINE(SIGABRT), + CONST_DEFINE(SIGIOT), + CONST_DEFINE(SIGBUS), + CONST_DEFINE(SIGFPE), + CONST_DEFINE(SIGKILL), + CONST_DEFINE(SIGUSR1), + CONST_DEFINE(SIGSEGV), + CONST_DEFINE(SIGUSR2), + CONST_DEFINE(SIGPIPE), + CONST_DEFINE(SIGALRM), + CONST_DEFINE(SIGTERM), + CONST_DEFINE(SIGSTKFLT), + CONST_DEFINE(SIGCHLD), + CONST_DEFINE(SIGCONT), + CONST_DEFINE(SIGSTOP), + CONST_DEFINE(SIGTSTP), + CONST_DEFINE(SIGTTIN), + CONST_DEFINE(SIGTTOU), + CONST_DEFINE(SIGURG), + CONST_DEFINE(SIGXCPU), + CONST_DEFINE(SIGXFSZ), + CONST_DEFINE(SIGVTALRM), + CONST_DEFINE(SIGPROF), + CONST_DEFINE(SIGWINCH), + CONST_DEFINE(SIGIO), + CONST_DEFINE(SIGPOLL), + CONST_DEFINE(SIGPWR), + CONST_DEFINE(SIGSYS), + { NULL } +}; + +int luaopen_signal(lua_State *L) +{ + lua_newtable(L); + setconst(L, signals); + return 1; +} From eac9574145348998608787d237cc095dc43c51db Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Fri, 22 Aug 2025 12:50:30 +0800 Subject: [PATCH 019/110] lua-lsm: api - add kernel library Signed-off-by: Tianjia Zhang --- security/lua/lua_kernel.c | 352 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 352 insertions(+) create mode 100644 security/lua/lua_kernel.c diff --git a/security/lua/lua_kernel.c b/security/lua/lua_kernel.c new file mode 100644 index 000000000000..e32a3f1e627c --- /dev/null +++ b/security/lua/lua_kernel.c @@ -0,0 +1,352 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Lua based LSM + * + * Copyright (C) 2025 The Alibaba Cloud Linux Authors. + */ + +#include "debug.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "lsm.h" +#include "auxlib.h" +#include "kvcache.h" +#include "lua_object.h" + + +/********************************** cred **********************************/ + +static int kernel_cred_uids(lua_State *L) +{ + const struct cred *cred = tocred(L, 1); + lua_pushinteger(L, cred->uid.val); + lua_pushinteger(L, cred->euid.val); + lua_pushinteger(L, cred->suid.val); + lua_pushinteger(L, cred->fsuid.val); + return 4; +} + +static int kernel_cred_gids(lua_State *L) +{ + const struct cred *cred = tocred(L, 1); + lua_pushinteger(L, cred->gid.val); + lua_pushinteger(L, cred->egid.val); + lua_pushinteger(L, cred->sgid.val); + lua_pushinteger(L, cred->fsgid.val); + return 4; +} + +static int kernel_cred_cap_eip(lua_State *L) +{ + struct cred *cred = tocred(L, 1); + int top = lua_gettop(L); + if (top > 4) + return luaL_error(L, "wrong number of arguments"); + if (top == 1) { + /* get caps */ + *newcap(L) = cred->cap_effective; + *newcap(L) = cred->cap_inheritable; + *newcap(L) = cred->cap_permitted; + return 3; + } + /* set caps */ + if (top >= 2 && !lua_isnil(L, 2)) + cred->cap_effective = tocap(L, 2); + if (top >= 3 && !lua_isnil(L, 3)) + cred->cap_inheritable = tocap(L, 3); + if (top == 4 && !lua_isnil(L, 4)) + cred->cap_permitted = tocap(L, 4); + lua_settop(L, 1); + return 1; +} + +static int kernel_cred_cap_bset(lua_State *L) +{ + struct cred *cred = tocred(L, 1); + if (lua_gettop(L) == 1) { + *newcap(L) = cred->cap_bset; + return 1; + } else { + cred->cap_bset = tocap(L, 2); + lua_settop(L, 1); + return 1; + } +} + +static int kernel_cred_cap_ambient(lua_State *L) +{ + struct cred *cred = tocred(L, 1); + if (lua_gettop(L) == 1) { + *newcap(L) = cred->cap_ambient; + return 1; + } else { + cred->cap_ambient = tocap(L, 2); + lua_settop(L, 1); + return 1; + } +} + +static const luaL_Reg cred_meth[] = { + { "uids", kernel_cred_uids }, + { "gids", kernel_cred_gids }, + { "cap_eip", kernel_cred_cap_eip }, + { "cap_bset", kernel_cred_cap_bset }, + { "cap_ambient", kernel_cred_cap_ambient }, + { NULL, NULL } +}; + +/********************************** task **********************************/ + + +static int kernel_task_pids(lua_State *L) +{ + struct task_struct *task = totask(L, 1); + lua_pushinteger(L, task->pid); + lua_pushinteger(L, task->tgid); + return 2; +} + +static int kernel_task_cred(lua_State *L) +{ + struct task_struct *task = totask(L, 1); + *(const struct cred **)newcred(L) = get_task_cred(task); + settopfenvfrom(L, 1); + return 1; +} + +static int kernel_task_comm(lua_State *L) +{ + struct task_struct *task = totask(L, 1); + lua_pushstring(L, task->comm); + return 1; +} + +static int kernel_task_nr_threads(lua_State *L) +{ + struct task_struct *task = totask(L, 1); + lua_pushinteger(L, get_nr_threads(task)); + return 1; +} + +static int kernel_task_group_leader(lua_State *L) +{ + struct task_struct *task = totask(L, 1); + if (!thread_group_leader(task)) { + *newtask(L) = rcu_dereference(task->group_leader); + settopfenvfrom(L, 1); + } else { + lua_settop(L, 1); + } + return 1; +} + +static int kernel_task_thread_group_leader(lua_State *L) +{ + struct task_struct *task = totask(L, 1); + lua_pushboolean(L, thread_group_leader(task)); + return 1; +} + +static int kernel_task_same_thread_group(lua_State *L) +{ + struct task_struct *task1 = totask(L, 1); + struct task_struct *task2 = totask(L, 2); + lua_pushboolean(L, same_thread_group(task1, task2)); + return 1; +} + +static int kernel_task_ptrace_parent(lua_State *L) +{ + struct task_struct *task = totask(L, 1); + struct task_struct *parent = ptrace_parent(task); + if (parent == NULL) + return 0; + *newtask(L) = parent; + settopfenvfrom(L, 1); + return 1; +} + +static int kernel_task_is_idle(lua_State *L) +{ + struct task_struct *task = totask(L, 1); + lua_pushboolean(L, is_idle_task(task)); + return 1; +} + +static int kernel_task_exe_file(lua_State *L) +{ + struct task_struct *task = totask(L, 1); + struct file *exe_file = get_task_exe_file(task); + if (exe_file == NULL) + return 0; + *newfile(L) = exe_file; + return 1; +} + +static const luaL_Reg task_meth[] = { + { "pids", kernel_task_pids }, + { "cred", kernel_task_cred }, + { "comm", kernel_task_comm }, + { "nr_threads", kernel_task_nr_threads }, + { "group_leader", kernel_task_group_leader }, + { "thread_group_leader", kernel_task_thread_group_leader }, + { "same_thread_group", kernel_task_same_thread_group }, + { "ptrace_parent", kernel_task_ptrace_parent }, + { "is_idle", kernel_task_is_idle }, + { "exe_file", kernel_task_exe_file }, + { NULL, NULL } +}; + +/********************************** lib **********************************/ + +static int kernel_version(lua_State *L) +{ + lua_pushinteger(L, LINUX_VERSION_MAJOR); + lua_pushinteger(L, LINUX_VERSION_PATCHLEVEL); + lua_pushinteger(L, LINUX_VERSION_SUBLEVEL); + return 3; +} + +static int kernel_lsm_funcs(lua_State *L) +{ + static const struct { + const char *funcname; + const char *rtype; + int nargs; + } lsm_funcs[] = { + #define LSM_HOOK(RET, DEFAULT, NAME, ...) \ + { #NAME, #RET, COUNT_ARGS(__VA_ARGS__) }, + #include + #undef LSM_HOOK + }; + int i; + lua_createtable(L, ARRAY_SIZE(lsm_funcs), 0); + for (i = 0; i < ARRAY_SIZE(lsm_funcs); i++) { + lua_createtable(L, 3, 0); + lua_pushstring(L, lsm_funcs[i].funcname); + lua_rawseti(L, -2, 1); + lua_pushstring(L, lsm_funcs[i].rtype); + lua_rawseti(L, -2, 2); + lua_pushinteger(L, lsm_funcs[i].nargs); + lua_rawseti(L, -2, 3); + /* res[i + 1] = { funcname, rtype, nargs } */ + lua_rawseti(L, -2, i + 1); + } + return 1; +} + +static int kernel_random(lua_State *L) +{ + int l, u; + u32 r; + switch (lua_gettop(L)) { + case 0: + r = get_random_u32(); + break; + case 1: + u = luaL_checkint(L, 1); + luaL_argcheck(L, 0 <= u, 1, "interval is empty"); + r = get_random_u32_below((u32)u); + break; + case 2: + l = luaL_checkint(L, 1); + u = luaL_checkint(L, 2); + luaL_argcheck(L, l <= u, 2, "interval is empty"); + r = get_random_u32_inclusive((u32)l, (u32)u); + break; + default: + return luaL_error(L, "wrong number of arguments"); + } + lua_pushinteger(L, r); + return 1; +} + +static int kernel_ktime_seconds(lua_State *L) +{ + int monotonic = lua_toboolean(L, 1); + time64_t sec; + if (monotonic) + sec = ktime_get_seconds(); + else + sec = ktime_get_real_seconds(); + lua_pushnumber(L, sec); + return 1; +} + +static int kernel_rcu_read_lock(lua_State *L) +{ + rcu_read_lock(); + return 0; +} + +static int kernel_rcu_read_unlock(lua_State *L) +{ + rcu_read_unlock(); + return 0; +} + +static int kernel_printk(lua_State *L) +{ + const char *s = luaL_checkstring(L, 1); + pr_info("%s\n", s); + return 0; +} + +#define DEF_PRINTK_LEVEL(name) \ + static int kernel_pr_ ## name(lua_State *L) \ + { \ + const char *s = luaL_checkstring(L, 1); \ + pr_ ## name("%s\n", s); \ + return 0; \ + } + +#define PRINTK_LEVEL_LISTS \ + XX(emerg) \ + XX(alert) \ + XX(crit) \ + XX(err) \ + XX(warn) \ + XX(notice) \ + XX(info) \ + XX(cont) \ + XX(devel) \ + XX(debug) + +#define XX(name) DEF_PRINTK_LEVEL(name) +PRINTK_LEVEL_LISTS +#undef XX + +static const luaL_Reg kernellib[] = { + { "version", kernel_version }, + { "lsm_funcs", kernel_lsm_funcs }, + { "random", kernel_random }, + { "ktime_seconds", kernel_ktime_seconds }, + { "rcu_read_lock", kernel_rcu_read_lock }, + { "rcu_read_unlock", kernel_rcu_read_unlock }, + { "printk", kernel_printk }, + +#define XX(name) { "pr_" #name, kernel_pr_ ## name }, + PRINTK_LEVEL_LISTS +#undef XX + + { NULL, NULL } +}; + +LUALIB_API int luaopen_kernel(lua_State *L) +{ + luaL_newlib(L, kernellib); + create_task_meta(L, task_meth); + create_cred_meta(L, cred_meth); + create_perfevent_meta(L, NULL); + create_ipc_meta(L, NULL); + create_msgmsg_meta(L, NULL); + create_key_meta(L, NULL); + create_bdev_meta(L, NULL); + return 1; +} From 67f1cc4a37657e7764628f02e0dc1bff0159724a Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Fri, 22 Aug 2025 12:50:50 +0800 Subject: [PATCH 020/110] lua-lsm: api - add filesystem library Signed-off-by: Tianjia Zhang --- security/lua/lua_fs.c | 432 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 432 insertions(+) create mode 100644 security/lua/lua_fs.c diff --git a/security/lua/lua_fs.c b/security/lua/lua_fs.c new file mode 100644 index 000000000000..ebc0224719ad --- /dev/null +++ b/security/lua/lua_fs.c @@ -0,0 +1,432 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Lua based LSM + * + * Copyright (C) 2025 The Alibaba Cloud Linux Authors. + */ + +#include "debug.h" +#include +#include +#include +#include +#include +#include +#include +#include "lsm.h" +#include "auxlib.h" +#include "kvcache.h" +#include "lua_object.h" + +#define FS_PATH_LEN 256 + +/********************************** util **********************************/ + +static int fs_xattr(lua_State *L, struct dentry *dentry, + struct inode *inode, const char *name) +{ + char buffer[128]; + ssize_t len; + + if (!(inode->i_opflags & IOP_XATTR)) + return 0; + + dget(dentry); + len = __vfs_getxattr(dentry, inode, name, buffer, sizeof(buffer)); + dput(dentry); + if (len == 0) { + lua_pushnil(L); + return 1; + } else if (len < 0) { + lua_pushnil(L); + lua_pushinteger(L, -len); + return 2; + } else { + lua_pushlstring(L, (const char *)buffer, len); + return 1; + } +} + +/********************************** dentry **********************************/ + +static int fs_dentry_dget(lua_State *L) +{ + struct dentry *dentry = todentry(L, 1); + dget(dentry); + lua_settop(L, 1); + return 1; +} + +static int fs_dentry_dput(lua_State *L) +{ + struct dentry *dentry = todentry(L, 1); + dput(dentry); + return 0; +} + +static int fs_dentry_backing_inode(lua_State *L) +{ + struct dentry *dentry = todentry(L, 1); + struct inode **inodep = newinode(L); + *inodep = d_backing_inode(dentry); + settopfenvfrom(L, 1); + return 1; +} + +static int fs_dentry_path(lua_State *L) +{ + struct dentry *dentry = todentry(L, 1); + int use_path = lua_toboolean(L, 2); + char buffer[FS_PATH_LEN]; + char *path; + if (use_path) + path = dentry_path(dentry, buffer, sizeof(buffer)); + else + path = dentry_path_raw(dentry, buffer, sizeof(buffer)); + if (path == NULL) + return 0; + lua_pushstring(L, path); + return 1; +} + +static int fs_dentry_xattr(lua_State *L) +{ + struct dentry *dentry = todentry(L, 1); + struct inode *inode = toinode(L, 2); + const char *name = luaL_checkstring(L, 3); + return fs_xattr(L, dentry, inode, name); +} + +static const luaL_Reg fs_dentry_meth[] = { + { "dget", fs_dentry_dget }, + { "dput", fs_dentry_dput }, + { "backing_inode", fs_dentry_backing_inode }, + { "path", fs_dentry_path }, + { "xattr", fs_dentry_xattr }, + { NULL, NULL } +}; + +/********************************** inode **********************************/ + +static int fs_inode_ino(lua_State *L) +{ + struct inode *inode = toinode(L, 1); + lua_pushinteger(L, inode->i_ino); + return 1; +} + +static int fs_inode_xattr(lua_State *L) +{ + struct inode *inode = toinode(L, 1); + struct dentry *dentry = todentry(L, 2); + const char *name = luaL_checkstring(L, 3); + return fs_xattr(L, dentry, inode, name); +} + +static int fs_inode_size(lua_State *L) +{ + struct inode *inode = toinode(L, 1); + lua_pushinteger(L, i_size_read(inode)); + return 1; +} + +static const luaL_Reg fs_inode_meth[] = { + { "ino", fs_inode_ino }, + { "xattr", fs_inode_xattr }, + { "size", fs_inode_size }, + { NULL, NULL } +}; + +/********************************** file **********************************/ + +static int fs_file_fput(lua_State *L) +{ + struct file *file = tofile(L, 1); + fput(file); + return 0; +} + +static int fs_file_path(lua_State *L) +{ + struct file *file = tofile(L, 1); + char buffer[FS_PATH_LEN]; + char *path = file_path(file, buffer, sizeof(buffer)); + if (IS_ERR(path)) { + lua_pushnil(L); + lua_pushinteger(L, PTR_ERR(path)); + return 2; + } + lua_pushstring(L, path); + return 1; +} + +static int fs_file_dentry(lua_State *L) +{ + const struct file *file = tofile(L, 1); + struct dentry **dentryp = newdentry(L); + *dentryp = file_dentry(file); + settopfenvfrom(L, 1); + return 1; +} + +static int fs_file_inode(lua_State *L) +{ + struct file *file = tofile(L, 1); + struct inode **inodep = newinode(L); + *inodep = file_inode(file); + settopfenvfrom(L, 1); + return 1; +} + +static int fs_file_xattr(lua_State *L) +{ + struct file *file = tofile(L, 1); + const char *name = luaL_checkstring(L, 2); + return fs_xattr(L, file_dentry(file), file_inode(file), name); +} + +static int fs_file_size(lua_State *L) +{ + struct file *file = tofile(L, 1); + lua_pushinteger(L, i_size_read(file_inode(file))); + return 1; +} + +/* + * table = file:fmode() + * bool = file:fmode('read') + * bool = file:fmode(true, 'read', 'exec') + */ +static int fs_file_fmode(lua_State *L) +{ + static const struct cflag_opt opts[] = { + { "read", FMODE_READ }, + { "write", FMODE_WRITE }, + { "exec", FMODE_EXEC }, + { "opened", FMODE_OPENED }, + { "created", FMODE_CREATED }, + { NULL, 0 } + }; + struct file *file = tofile(L, 1); + int top = lua_gettop(L); + if (top >= 2) { + int start = (top == 2) ? 2 : 3; + int and = lua_toboolean(L, 2); + fmode_t flags = tocflags(L, start, top, opts, 0); + fmode_t res = file->f_mode & flags; + lua_pushboolean(L, and ? res == flags : (int)res); + return 1; + } else if (top == 1) { + table_fromopts(L, opts, 0, (unsigned int)file->f_mode); + return 1; + } + return 0; +} + +static const luaL_Reg fs_file_meth[] = { + { "fput", fs_file_fput }, + { "path", fs_file_path }, + { "dentry", fs_file_dentry }, + { "inode", fs_file_inode }, + { "xattr", fs_file_xattr }, + { "size", fs_file_size }, + { "fmode", fs_file_fmode }, + { NULL, NULL } +}; + +/********************************** binprm **********************************/ + +#define DEF_BINPRM_FIELD(type, name) \ + static int fs_binprm_ ## name(lua_State *L) \ + { \ + struct linux_binprm *bprm = tobinprm(L, 1); \ + if (bprm->name) { \ + *new ## type(L) = bprm->name; \ + /* setfenv(file, getfenv(binprm)) */ \ + settopfenvfrom(L, 1); \ + return 1; \ + } else { \ + return 0; \ + } \ + } + +DEF_BINPRM_FIELD(file, executable) +DEF_BINPRM_FIELD(file, interpreter) +DEF_BINPRM_FIELD(file, file) +DEF_BINPRM_FIELD(cred, cred) + +static const luaL_Reg fs_binprm_meth[] = { + { "executable", fs_binprm_executable }, + { "interpreter", fs_binprm_interpreter }, + { "file", fs_binprm_file }, + { "cred", fs_binprm_cred }, + { NULL, NULL } +}; + +/*********************************** path ************************************/ + +static int fs_path_vfsmount(lua_State *L) +{ + struct path *path = topath(L, 1); + *newvfsmount(L) = path->mnt; + settopfenvfrom(L, 1); + return 1; +} + +static int fs_path_dentry(lua_State *L) +{ + struct path *path = topath(L, 1); + *newdentry(L) = path->dentry; + settopfenvfrom(L, 1); + return 1; +} + +static int fs_path_eq(lua_State *L) +{ + struct path *path1 = topath(L, 1); + struct path *path2 = topath(L, 2); + lua_pushboolean(L, path_equal(path1, path2)); + return 1; +} + +static const luaL_Reg fs_path_meth[] = { + { "vfsmount", fs_path_vfsmount }, + { "dentry", fs_path_dentry }, + { "__eq", fs_path_eq }, + { NULL, NULL } +}; + +/******************************** super_block ********************************/ + +static int fs_superblock_magic(lua_State *L) +{ + struct super_block *sb = tosuperblock(L, 1); + lua_pushinteger(L, sb->s_magic); + return 1; +} + +static int fs_superblock_root(lua_State *L) +{ + struct super_block *sb = tosuperblock(L, 1); + *newdentry(L) = sb->s_root; + settopfenvfrom(L, 1); + return 1; +} + +static int fs_superblock_fstype_name(lua_State *L) +{ + struct super_block *sb = tosuperblock(L, 1); + lua_pushstring(L, sb->s_type->name); + return 1; +} + +static const luaL_Reg fs_superblock_meth[] = { + { "magic", fs_superblock_magic }, + { "root", fs_superblock_root }, + { "fstype_name", fs_superblock_fstype_name }, + { NULL, NULL } +}; + +/******************************** fs_context *********************************/ + +static int fs_fscontext_parse_fs_string(lua_State *L) +{ + struct fs_context *fc = tofscontext(L, 1); + const char *key = luaL_checkstring(L, 2); + size_t size; + const char *value = luaL_checklstring(L, 3, &size); + int rc = vfs_parse_fs_string(fc, key, value, size); + lua_pushboolean(L, rc == 0); + return 1; +} + +static const luaL_Reg fs_fscontext_meth[] = { + { "parse_fs_string", fs_fscontext_parse_fs_string }, + { NULL, NULL } +}; + +/********************************* vfsmount **********************************/ + +static int fs_vfsmount_superblock(lua_State *L) +{ + struct vfsmount *mnt = tovfsmount(L, 1); + *newsuperblock(L) = mnt->mnt_sb; + return 1; +} + +static int fs_vfsmount_mntidmap(lua_State *L) +{ + struct vfsmount *mnt = tovfsmount(L, 1); + *newmntidmap(L) = mnt_idmap(mnt); + return 1; +} + +static const luaL_Reg fs_vfsmount_meth[] = { + { "superblock", fs_vfsmount_superblock }, + { "mntidmap", fs_vfsmount_mntidmap }, + { NULL, NULL } +}; + +/********************************* mnt_idmap *********************************/ + +static int fs_mntidmap_inode_owner_or_capable(lua_State *L) +{ + struct mnt_idmap *idmap = tomntidmap(L, 1); + const struct inode *inode = toinode(L, 2); + lua_pushboolean(L, inode_owner_or_capable(idmap, inode)); + return 1; +} + +static int fs_mntidmap_capable_wrt_inode_uidgid(lua_State *L) +{ + struct mnt_idmap *idmap = tomntidmap(L, 1); + const struct inode *inode = toinode(L, 2); + int cap = luaL_checkint(L, 3); + lua_pushboolean(L, capable_wrt_inode_uidgid(idmap, inode, cap)); + return 1; +} + +static const luaL_Reg fs_mntidmap_meth[] = { + { "inode_owner_or_capable", fs_mntidmap_inode_owner_or_capable }, + { "capable_wrt_inode_uidgid", fs_mntidmap_capable_wrt_inode_uidgid }, + { NULL, NULL } +}; + +/************************************ lib ************************************/ + +static int fs_filp_open(lua_State *L) +{ + const char *filename = luaL_checkstring(L, 1); + int flags = luaL_checkinteger(L, 2); + umode_t mode = luaL_checkinteger(L, 3); + struct file **filp = newfile(L); + *filp = filp_open(filename, flags, mode); + if (IS_ERR(*filp)) { + lua_pushnil(L); + lua_pushinteger(L, PTR_ERR(*filp)); + return 2; + } + return 1; +} + +static const luaL_Reg fslib[] = { + { "filp_open", fs_filp_open }, + { NULL, NULL } +}; + +LUALIB_API int luaopen_fs(lua_State *L) +{ + luaL_newlib(L, fslib); + + create_dentry_meta(L, fs_dentry_meth); + create_inode_meta(L, fs_inode_meth); + create_file_meta(L, fs_file_meth); + create_binprm_meta(L, fs_binprm_meth); + create_path_meta(L, fs_path_meth); + create_superblock_meta(L, fs_superblock_meth); + create_fscontext_meta(L, fs_fscontext_meth); + create_vfsmount_meta(L, fs_vfsmount_meth); + create_mntidmap_meta(L, fs_mntidmap_meth); + + return 1; +} From df875cad6488bb182a2b81e5463d153125671de8 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Fri, 22 Aug 2025 12:51:07 +0800 Subject: [PATCH 021/110] lua-lsm: api - add net library Signed-off-by: Tianjia Zhang --- security/lua/lua_net.c | 351 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 351 insertions(+) create mode 100644 security/lua/lua_net.c diff --git a/security/lua/lua_net.c b/security/lua/lua_net.c new file mode 100644 index 000000000000..1bb308351334 --- /dev/null +++ b/security/lua/lua_net.c @@ -0,0 +1,351 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Lua based LSM + * + * Copyright (C) 2025 The Alibaba Cloud Linux Authors. + */ + +#include "debug.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "lsm.h" +#include "auxlib.h" +#include "kvcache.h" +#include "lua_object.h" + +static const char *family_tostring(sa_family_t sa_family) +{ + const char *family = NULL; + switch (sa_family) { + case AF_UNSPEC: family = "unspec"; break; + case AF_INET: family = "inet"; break; + case AF_INET6: family = "inet6"; break; + case AF_UNIX: family = "unix"; break; + case AF_NETLINK: family = "netlink"; break; + case AF_PACKET: family = "packet"; break; + case AF_KEY: family = "key"; break; + case AF_APPLETALK: family = "appletalk"; break; + case AF_ALG: family = "alg"; break; + case AF_NFC: family = "nfc"; break; + case AF_VSOCK: family = "vsock"; break; + case AF_KCM: family = "kcm"; break; + case AF_SMC: family = "smc"; break; + } + return family; +} + +/*********************************** sock ***********************************/ + +static int net_sock_socket(lua_State *L) +{ + struct sock *sk = tosock(L, 1); + *newsocket(L) = sk->sk_socket; + return 1; +} + +static int net_sock_suites(lua_State *L) +{ + struct sock *sk = tosock(L, 1); + const char *family = "unknown", *type = "unknown", *protocol = "unknown"; + int nres = 0; + if (lua_gettop(L) >= 2) { + if (!lua_toboolean(L, 2)) + family = NULL; + if (!lua_toboolean(L, 3)) + type = NULL; + if (!lua_toboolean(L, 4)) + protocol = NULL; + } + if (family) { + family = family_tostring(sk->sk_family); + if (family == NULL) + family = "unknown"; + lua_pushstring(L, family); + nres += 1; + } + if (type) { + switch (sk->sk_type) { + case SOCK_STREAM: type = "stream"; break; + case SOCK_DGRAM: type = "dgram"; break; + case SOCK_RAW: type = "raw"; break; + case SOCK_RDM: type = "rdm"; break; + case SOCK_SEQPACKET: type = "seqpacket"; break; + case SOCK_DCCP: type = "dccp"; break; + case SOCK_PACKET: type = "packet"; break; + } + lua_pushstring(L, type); + nres += 1; + } + if (protocol) { + switch (sk->sk_protocol) { + case IPPROTO_IP: protocol = "ip"; break; + case IPPROTO_ICMP: protocol = "icmp"; break; + case IPPROTO_IGMP: protocol = "igmp"; break; + case IPPROTO_TCP: protocol = "tcp"; break; + case IPPROTO_EGP: protocol = "egp"; break; + case IPPROTO_UDP: protocol = "udp"; break; + case IPPROTO_DCCP: protocol = "dccp"; break; + case IPPROTO_IPV6: protocol = "ipv6"; break; + case IPPROTO_ESP: protocol = "esp"; break; + case IPPROTO_L2TP: protocol = "l2tp"; break; + case IPPROTO_SCTP: protocol = "sctp"; break; + case IPPROTO_UDPLITE: protocol = "udplite"; break; + case IPPROTO_RAW: protocol = "raw"; break; + case IPPROTO_SMC: protocol = "smc"; break; + case IPPROTO_MPTCP: protocol = "mptcp"; break; + } + lua_pushstring(L, protocol); + nres += 1; + } + return nres; +} + +static int net_sock_listener(lua_State *L) +{ + struct sock *sk = tosock(L, 1); + int time_wait = lua_toboolean(L, 2); + lua_pushboolean(L, time_wait ? sk_listener_or_tw(sk) : sk_listener(sk)); + return 1; +} + +#define SOCK_BOOL_DEF(name) \ + static int net_sock_is_ ## name(lua_State *L) \ + { \ + struct sock *sk = tosock(L, 1); \ + lua_pushboolean(L, sk_is_ ## name(sk)); \ + return 1; \ + } + +SOCK_BOOL_DEF(inet) +SOCK_BOOL_DEF(tcp) +SOCK_BOOL_DEF(udp) +SOCK_BOOL_DEF(stream_unix) +SOCK_BOOL_DEF(vsock) + +static const luaL_Reg sock_meth[] = { + { "socket", net_sock_socket }, + { "suites", net_sock_suites }, + { "listener", net_sock_listener }, + { "is_inet", net_sock_is_inet }, + { "is_tcp", net_sock_is_tcp }, + { "is_udp", net_sock_is_udp }, + { "is_stream_unix", net_sock_is_stream_unix }, + { "is_vsock", net_sock_is_vsock }, + { NULL, NULL } +}; + +/********************************** socket **********************************/ + +static int net_socket_release(lua_State *L) +{ + struct socket **sockp = tosocketp(L, 1); + if (*sockp) { + sock_release(*sockp); + *sockp = NULL; + } + return 0; +} + +static int net_socket_sock(lua_State *L) +{ + struct socket *sock = tosocket(L, 1); + *newsock(L) = sock->sk; + return 1; +} + +static int net_socket_inode(lua_State *L) +{ + struct socket *sock = tosocket(L, 1); + *newinode(L) = SOCK_INODE(sock); + return 1; +} + +static const luaL_Reg socket_meth[] = { + { "release", net_socket_release }, + { "sock", net_socket_sock }, + { "inode", net_socket_inode }, + { NULL, NULL } +}; + +/********************************** sk_buff *********************************/ + +static int net_skb_sock(lua_State *L) +{ + struct sk_buff *skb = toskb(L, 1); + *newsock(L) = skb->sk; + return 1; +} + +static int net_skb_full_sk(lua_State *L) +{ + struct sk_buff *skb = toskb(L, 1); + *newsock(L) = skb_to_full_sk(skb); + return 1; +} + +static int net_skb_protocol(lua_State *L) +{ + struct sk_buff *skb = toskb(L, 1); + const char *protocol = "unknown"; + switch (skb->protocol) { + case htons(ETH_P_LOOP): protocol = "loop"; break; + case htons(ETH_P_IP): protocol = "ip"; break; + case htons(ETH_P_IPV6): protocol = "ipv6"; break; + case htons(ETH_P_ARP): protocol = "arp"; break; + case htons(ETH_P_RARP): protocol = "rarp"; break; + } + lua_pushstring(L, protocol); + return 1; +} + +static int net_skb_iif(lua_State *L) +{ + struct sk_buff *skb = toskb(L, 1); + lua_pushinteger(L, skb->skb_iif); + return 1; +} + +static int net_skb_secmark(lua_State *L) +{ + struct sk_buff *skb = toskb(L, 1); + lua_pushinteger(L, skb->secmark); + return 1; +} + +static const luaL_Reg skb_meth[] = { + { "sock", net_skb_sock }, + { "full_sk", net_skb_full_sk }, + { "protocol", net_skb_protocol }, + { "iif", net_skb_iif }, + { "secmark", net_skb_secmark }, + { NULL, NULL } +}; + +/********************************** sockaddr ********************************/ + +static int sockaddr_family(lua_State *L) +{ + struct sockaddr *sa = tosockaddr(L, 1); + const char *family = family_tostring(sa->sa_family); + if (family == NULL) + family = "unknown"; + lua_pushstring(L, family); + return 1; +} + +static int sockaddr_addrs(lua_State *L) +{ + struct sockaddr *sa = tosockaddr(L, 1); + switch (sa->sa_family) { + case AF_INET: + lua_pushstring(L, "inet"); + lua_pushinteger(L, ntohs(((struct sockaddr_in *)sa)->sin_port)); + lua_pushinteger(L, ((struct sockaddr_in *)sa)->sin_addr.s_addr); + return 3; + case AF_INET6: + lua_pushstring(L, "inet6"); + lua_pushinteger(L, ntohs(((struct sockaddr_in6 *)sa)->sin6_port)); + lua_pushlstring(L, ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr, + sizeof(((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr)); + return 3; + case AF_UNIX: + lua_pushstring(L, "unix"); + lua_pushstring(L, ((struct sockaddr_un *)sa)->sun_path); + return 2; + } + return 0; +} + +static const luaL_Reg sockaddr_meth[] = { + { "family", sockaddr_family }, + { "addrs", sockaddr_addrs }, + { NULL, NULL } +}; + +/************************************ lib ***********************************/ + +static int net_sock_alloc(lua_State *L) +{ + struct socket **sockp = newsocket(L); + *sockp = sock_alloc(); + if (*sockp == NULL) + return 0; + return 1; +} + +#define INET_HN_DEFINE(name) \ + static int net_ ## name(lua_State *L) \ + { \ + lua_Integer n = luaL_checkinteger(L, 1); \ + lua_pushinteger(L, (lua_Integer)name(n)); \ + return 1; \ + } + +INET_HN_DEFINE(htonl) +INET_HN_DEFINE(ntohl) +INET_HN_DEFINE(htons) +INET_HN_DEFINE(ntohs) + +static int net_in_aton(lua_State *L) +{ + const char *s = luaL_checkstring(L, 1); + lua_pushinteger(L, in_aton(s)); + return 1; +} + +static int net_in4_pton(lua_State *L) +{ + size_t len; + const char *s = luaL_checklstring(L, 1, &len); + __be32 addr; + if (in4_pton(s, (int)len, (u8 *)&addr, '\n', NULL)) + lua_pushinteger(L, addr); + else + lua_pushnil(L); + return 1; +} + +static int net_in6_pton(lua_State *L) +{ + size_t len; + const char *s = luaL_checklstring(L, 1, &len); + __u8 addr[16]; + if (in6_pton(s, (int)len, (u8 *)addr, '\n', NULL)) + lua_pushlstring(L, addr, sizeof(addr)); + else + lua_pushnil(L); + return 1; +} + +static const luaL_Reg netlib[] = { + { "sock_alloc", net_sock_alloc }, + { "htonl", net_htonl }, + { "ntohl", net_ntohl }, + { "htons", net_htons }, + { "ntohs", net_ntohs }, + { "in_aton", net_in_aton }, + { "in4_pton", net_in4_pton }, + { "in6_pton", net_in6_pton }, + { NULL, NULL } +}; + + +LUALIB_API int luaopen_net(lua_State *L) +{ + luaL_newlib(L, netlib); + + create_sock_meta(L, sock_meth); + create_socket_meta(L, socket_meth); + create_skb_meta(L, skb_meth); + create_sockaddr_meta(L, sockaddr_meth); + + return 1; +} From 3bef0ac2c9e6a963f1061eb4f4eab1bfad7e124a Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Fri, 22 Aug 2025 12:52:05 +0800 Subject: [PATCH 022/110] lua-lsm: Wrapper all LSM functions Signed-off-by: Tianjia Zhang --- security/lua/lsm_defs.c | 3512 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 3512 insertions(+) create mode 100644 security/lua/lsm_defs.c diff --git a/security/lua/lsm_defs.c b/security/lua/lsm_defs.c new file mode 100644 index 000000000000..8ac988f2056d --- /dev/null +++ b/security/lua/lsm_defs.c @@ -0,0 +1,3512 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Lua based LSM + * + * Copyright (C) 2025 The Alibaba Cloud Linux Authors. + */ + +#define pr_fmt(fmt) "lsm-defs: " fmt + +#include "debug.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for __MAP */ +#include /* for ktime_get */ +#include +#include +#include +#include +#include +#include +#include "lsm.h" +#include "lua_object.h" +#include "lsm_defs.h" + + +/* LSM_RET_DEFAULT */ +#define DECLARE_LSM_RET_DEFAULT_void(DEFAULT, NAME) \ + const int __maybe_unused LSM_RET_DEFAULT(NAME) = 0; +#define DECLARE_LSM_RET_DEFAULT_int(DEFAULT, NAME) \ + const int __maybe_unused LSM_RET_DEFAULT(NAME) = (DEFAULT); +#define LSM_HOOK(RET, DEFAULT, NAME, ...) \ + DECLARE_LSM_RET_DEFAULT_##RET(DEFAULT, NAME) + +#include +#undef LSM_HOOK + + +/** + * binder_set_context_mgr + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(binder_set_context_mgr, const struct cred *, mgr) +{ + *(const struct cred **)newcred(L) = mgr; +} + +/** + * binder_transaction + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(binder_transaction, const struct cred *, from, + const struct cred *, to) +{ + *(const struct cred **)newcred(L) = from; + *(const struct cred **)newcred(L) = to; +} + +/** + * binder_transfer_binder + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(binder_transfer_binder, const struct cred *, from, + const struct cred *, to) +{ + *(const struct cred **)newcred(L) = from; + *(const struct cred **)newcred(L) = to; +} + +/** + * binder_transfer_file + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(binder_transfer_file, const struct cred *, from, + const struct cred *, to, const struct file *, file) +{ + *(const struct cred **)newcred(L) = from; + *(const struct cred **)newcred(L) = to; + *(const struct file **)newfile(L) = file; +} + +/** + * ptrace_access_check + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(ptrace_access_check, struct task_struct *, child, + unsigned int, mode) +{ + static const struct cflag_opt opts[] = { + { "read", PTRACE_MODE_READ }, + { "attach", PTRACE_MODE_ATTACH }, + { "noaudit", PTRACE_MODE_NOAUDIT }, + { "fscreds", PTRACE_MODE_FSCREDS }, + { "realcreds", PTRACE_MODE_REALCREDS }, + { NULL, 0 } + }; + static const unsigned int flags = PTRACE_MODE_READ | + PTRACE_MODE_ATTACH | PTRACE_MODE_NOAUDIT | + PTRACE_MODE_FSCREDS | PTRACE_MODE_REALCREDS; + *newtask(L) = child; + table_fromopts(L, opts, flags, mode); +} + +/** + * ptrace_traceme + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(ptrace_traceme, struct task_struct *, parent) +{ + *newtask(L) = parent; +} + +/** + * capget + * Default: 0 + * + * hooks result format: + * [any] : -EPERM + * false, errno : -errno + * nil, errno : -errno + * cap_e, cap_i, cap_p: 0, must be cap class + */ +LUA_LSM_INT_NAKED_DEFINE4(capget, const struct task_struct *, target, + kernel_cap_t *, effective, kernel_cap_t *, inheritable, + kernel_cap_t *, permitted) +{ + int top = lua_gettop(L); + int ret = LSM_RET_DEFAULT(capget); + int status; + + *(const struct task_struct **)newtask(L) = target; + + lua_pushvalue(L, -1 - 4); /* env */ + lua_setfenv(L, -1 - 6); /* restore thread.env */ + + status = lua_pcall(L, 1, LUA_MULTRET, -1 - 6); + if (status != 0) { + const char * __maybe_unused error = lua_tostring(L, -1); + __log_err("pcall: status = %d, top = %d, %s\n", + status, lua_gettop(L), error); + } else { + /* stack: [traceback, thread, env, _MODULES, _M, res1, ...] */ + int nres = lua_gettop(L) - top + 1; + kernel_cap_t *e, *i, *p; + switch (nres) { + case 0: + break; + case 1: + if (lua_type(L, top) == LUA_TBOOLEAN) + ret = lua_toboolean(L, top) ? 0 : -EPERM; + break; + case 2: + if (!lua_toboolean(L, top)) { + int errno = lua_tointeger(L, top + 1); + if (errno > 0 && errno <= MAX_ERRNO) + ret = -errno; + } + break; + default: + e = checkudata_cap(L, top); + i = checkudata_cap(L, top + 1); + p = checkudata_cap(L, top + 2); + if (e == NULL || i == NULL || p == NULL) + break; + *effective = *e; + *inheritable = *i; + *permitted = *p; + ret = 0; + break; + } + } + return ret; +} + +/** + * capset + * Default: 0 + */ +LUA_LSM_INT_DEFINE5(capset, struct cred *, new, const struct cred *, old, + const kernel_cap_t *, effective, + const kernel_cap_t *, inheritable, + const kernel_cap_t *, permitted) +{ + *newcred(L) = new; + *(const struct cred **)newcred(L) = old; + *newcap(L) = *effective; + *newcap(L) = *inheritable; + *newcap(L) = *permitted; +} + +/** + * TODO: capable + * Default: 0 + */ +LUA_LSM_INT_DEFINE4(capable, const struct cred *, cred, + struct user_namespace *, ns, int, cap, unsigned int, opts) +{ + *(const struct cred **)newcred(L) = cred; + lua_pushnil(L); /* TODO: ns */ + lua_pushinteger(L, (lua_Integer)cap); + lua_pushinteger(L, (lua_Integer)opts); +} + +/** + * quotactl + * Default: 0 + */ +LUA_LSM_INT_DEFINE4(quotactl, int, cmds, int, type, int, id, + const struct super_block *, sb) +{ + lua_pushinteger(L, (lua_Integer)cmds); + lua_pushinteger(L, (lua_Integer)type); + lua_pushinteger(L, (lua_Integer)id); + *(const struct super_block **)newsuperblock(L) = sb; +} + +/** + * quota_on + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(quota_on, struct dentry *, dentry) +{ + *newdentry(L) = dentry; +} + +/** + * syslog + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(syslog, int, type) +{ + lua_pushinteger(L, (lua_Integer)type); +} + +/** + * TODO: settime + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(settime, const struct timespec64 *, ts, + const struct timezone *, tz) +{ + lua_pushnil(L); /* TODO: ts */ + lua_pushnil(L); /* TODO: tz */ +} + +/** + * TODO: vm_enough_memory + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(vm_enough_memory, struct mm_struct *, mm, long, pages) +{ + lua_pushnil(L); /* TODO: mm */ + lua_pushnil(L); /* TODO: pages */ +} + +/** + * bprm_creds_for_exec + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(bprm_creds_for_exec, struct linux_binprm *, bprm) +{ + *newbinprm(L) = bprm; +} + +/** + * bprm_creds_from_file + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(bprm_creds_from_file, struct linux_binprm *, bprm, + const struct file *, file) +{ + *newbinprm(L) = bprm; + *(const struct file **)newfile(L) = file; +} + +/** + * bprm_check_security + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(bprm_check_security, struct linux_binprm *, bprm) +{ + *newbinprm(L) = bprm; +} + +/** + * bprm_committing_creds + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE1(bprm_committing_creds, const struct linux_binprm *, bprm) +{ + *(const struct linux_binprm **)newbinprm(L) = bprm; +} + +/** + * bprm_committed_creds + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE1(bprm_committed_creds, const struct linux_binprm *, bprm) +{ + *(const struct linux_binprm **)newbinprm(L) = bprm; +} + +/** + * fs_context_submount + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(fs_context_submount, struct fs_context *, fc, + struct super_block *, reference) +{ + *newfscontext(L) = fc; + *newsuperblock(L) = reference; +} + +/** + * fs_context_dup + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(fs_context_dup, struct fs_context *, fc, + struct fs_context *, src_sc) +{ + *newfscontext(L) = fc; + *newfscontext(L) = src_sc; +} + +/** + * TODO: fs_context_parse_param + * Default: -ENOPARAM + */ +LUA_LSM_INT_DEFINE2(fs_context_parse_param, struct fs_context *, fc, + struct fs_parameter *, param) +{ + *newfscontext(L) = fc; + lua_pushnil(L); /* TODO: param */ +} + +/** + * sb_alloc_security - prepare + */ +LUA_LSM_PREPARE_DEFINE1(sb_alloc_security, struct super_block *, sb) +{ + struct lua_lsm_object *llo = lua_lsm_superblock(sb); + kvcache_dict_init(&llo->dict); + return 0; +} + +/** + * sb_alloc_security + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(sb_alloc_security, struct super_block *, sb) +{ + *newsuperblock(L) = sb; +} + +/** + * sb_delete + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE1(sb_delete, struct super_block *, sb) +{ + *newsuperblock(L) = sb; +} + +/** + * sb_free_security - postpone + */ +LUA_LSM_POSTPONE_DEFINE1(sb_free_security, struct super_block *, sb) +{ + struct lua_lsm_object *llo = lua_lsm_superblock(sb); + kvcache_dict_free(&llo->dict); +} + +/** + * sb_free_security + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE1(sb_free_security, struct super_block *, sb) +{ + *newsuperblock(L) = sb; +} + +/** + * TODO: sb_free_mnt_opts + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE1(sb_free_mnt_opts, void *, mnt_opts) +{ + lua_pushnil(L); /* TODO: mnt_opts */ +} + +/** + * TODO: sb_eat_lsm_opts + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(sb_eat_lsm_opts, char *, orig, void **, mnt_opts) +{ + lua_pushstring(L, (const char *)orig); + lua_pushnil(L); /* TODO: mnt_opts */ +} + +/** + * TODO: sb_mnt_opts_compat + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(sb_mnt_opts_compat, struct super_block *, sb, + void *, mnt_opts) +{ + *newsuperblock(L) = sb; + lua_pushnil(L); /* TODO: mnt_opts */ +} + +/** + * TODO: sb_remount + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(sb_remount, struct super_block *, sb, void *, mnt_opts) +{ + *newsuperblock(L) = sb; + lua_pushnil(L); /* TODO: mnt_opts */ +} + +/** + * sb_kern_mount + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(sb_kern_mount, const struct super_block *, sb) +{ + *(const struct super_block **)newsuperblock(L) = sb; +} + +/** + * TODO: sb_show_options + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(sb_show_options, struct seq_file *, m, + struct super_block *, sb) +{ + lua_pushnil(L); /* TODO: m */ + *newsuperblock(L) = sb; +} + +/** + * sb_statfs + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(sb_statfs, struct dentry *, dentry) +{ + *newdentry(L) = dentry; +} + +/** + * TODO: sb_mount + * Default: 0 + */ +LUA_LSM_INT_DEFINE5(sb_mount, const char *, dev_name, const struct path *, path, + const char *, type, unsigned long, flags, void *, data) +{ + lua_pushstring(L, dev_name); + *(const struct path **)newpath(L) = path; + lua_pushstring(L, type); + lua_pushnumber(L, (lua_Number)flags); + lua_pushnil(L); /* TODO: data */ +} + +/** + * sb_umount + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(sb_umount, struct vfsmount *, mnt, int, flags) +{ + *newvfsmount(L) = mnt; + lua_pushinteger(L, (lua_Integer)flags); +} + +/** + * sb_pivotroot + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(sb_pivotroot, const struct path *, old_path, + const struct path *, new_path) +{ + *(const struct path **)newpath(L) = old_path; + *(const struct path **)newpath(L) = new_path; +} + +/** + * TODO: sb_set_mnt_opts + * Default: 0 + */ +LUA_LSM_INT_DEFINE4(sb_set_mnt_opts, struct super_block *, sb, void *, mnt_opts, + unsigned long, kern_flags, unsigned long *, set_kern_flags) +{ + *newsuperblock(L) = sb; + lua_pushnil(L); /* TODO: mnt_opts */ + lua_pushnumber(L, (lua_Number)kern_flags); + lua_pushnil(L); /* TODO: set_kern_flags */ +} + +/** + * TODO: sb_clone_mnt_opts + * Default: 0 + */ +LUA_LSM_INT_DEFINE4(sb_clone_mnt_opts, const struct super_block *, oldsb, + struct super_block *, newsb, unsigned long, kern_flags, + unsigned long *, set_kern_flags) +{ + *(const struct super_block **)newsuperblock(L) = oldsb; + *newsuperblock(L) = newsb; + lua_pushnumber(L, (lua_Number)kern_flags); + lua_pushnil(L); /* TODO: set_kern_flags */ +} + +/** + * move_mount + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(move_mount, const struct path *, from_path, + const struct path *, to_path) +{ + *(const struct path **)newpath(L) = from_path; + *(const struct path **)newpath(L) = to_path; +} + +/** + * TODO: dentry_init_security + * Default: -EOPNOTSUPP + */ +LUA_LSM_INT_DEFINE5(dentry_init_security, struct dentry *, dentry, + int, mode, const struct qstr *, name, + const char **, xattr_name, struct lsm_context *, cp) +{ + *newdentry(L) = dentry; + lua_pushinteger(L, (lua_Integer)mode); + lua_pushnil(L); /* TODO: name */ + lua_pushnil(L); /* TODO: xattr_name */ + lua_pushnil(L); /* TODO: cp */ +} + +/** + * TODO: dentry_create_files_as + * Default: 0 + */ +LUA_LSM_INT_DEFINE5(dentry_create_files_as, struct dentry *, dentry, + int, mode, struct qstr *, name, + const struct cred *, old, struct cred *, new) +{ + *newdentry(L) = dentry; + lua_pushinteger(L, (lua_Integer)mode); + lua_pushnil(L); /* TODO: name */ + *(const struct cred **)newcred(L) = old; + *newcred(L) = new; +} + +#ifdef CONFIG_SECURITY_PATH + +/** + * path_unlink + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(path_unlink, const struct path *, dir, + struct dentry *, dentry) +{ + *(const struct path **)newpath(L) = dir; + *newdentry(L) = dentry; +} + +/** + * path_mkdir + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(path_mkdir, const struct path *, dir, + struct dentry *, dentry, umode_t, mode) +{ + *(const struct path **)newpath(L) = dir; + *newdentry(L) = dentry; + lua_pushinteger(L, (lua_Integer)mode); +} + +/** + * path_rmdir + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(path_rmdir, const struct path *, dir, + struct dentry *, dentry) +{ + *(const struct path **)newpath(L) = dir; + *newdentry(L) = dentry; +} + +/** + * path_mknod + * Default: 0 + */ +LUA_LSM_INT_DEFINE4(path_mknod, const struct path *, dir, + struct dentry *, dentry, umode_t, mode, unsigned int, dev) +{ + *(const struct path **)newpath(L) = dir; + *newdentry(L) = dentry; + lua_pushinteger(L, (lua_Integer)mode); + lua_pushinteger(L, (lua_Integer)dev); +} + +/** + * path_post_mknod + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE2(path_post_mknod, struct mnt_idmap *, idmap, + struct dentry *, dentry) +{ + *newmntidmap(L) = idmap; + *newdentry(L) = dentry; +} + +/** + * path_truncate + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(path_truncate, const struct path *, path) +{ + *(const struct path **)newpath(L) = path; +} + +/** + * path_symlink + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(path_symlink, const struct path *, dir, + struct dentry *, dentry, const char *, old_name) +{ + *(const struct path **)newpath(L) = dir; + *newdentry(L) = dentry; + lua_pushstring(L, old_name); +} + +/** + * path_link + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(path_link, struct dentry *, old_dentry, + const struct path *, new_dir, struct dentry *, new_dentry) +{ + *newdentry(L) = old_dentry; + *(const struct path **)newpath(L) = new_dir; + *newdentry(L) = new_dentry; +} + +/** + * path_rename + * Default: 0 + */ +LUA_LSM_INT_DEFINE5(path_rename, const struct path *, old_dir, + struct dentry *, old_dentry, const struct path *, new_dir, + struct dentry *, new_dentry, unsigned int, flags) +{ + *(const struct path **)newpath(L) = old_dir; + *newdentry(L) = old_dentry; + *(const struct path **)newpath(L) = new_dir; + *newdentry(L) = new_dentry; + lua_pushinteger(L, (lua_Integer)flags); +} + +/** + * path_chmod + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(path_chmod, const struct path *, path, umode_t, mode) +{ + *(const struct path **)newpath(L) = path; + lua_pushinteger(L, (lua_Integer)mode); +} + +/** + * TODO: path_chown + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(path_chown, const struct path *, path, + kuid_t, uid, kgid_t, gid) +{ + *(const struct path **)newpath(L) = path; + lua_pushnil(L); /* TODO: uid */ + lua_pushnil(L); /* TODO: gid */ +} + +/** + * path_chroot + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(path_chroot, const struct path *, path) +{ + *(const struct path **)newpath(L) = path; +} + +#endif /* CONFIG_SECURITY_PATH */ + +/** + * path_notify + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(path_notify, const struct path *, path, + u64, mask, unsigned int, obj_type) +{ + *(const struct path **)newpath(L) = path; + lua_pushnumber(L, (lua_Number)mask); + lua_pushinteger(L, (lua_Integer)obj_type); +} + +/** + * inode_alloc_security - prepare + */ +LUA_LSM_PREPARE_DEFINE1(inode_alloc_security, struct inode *, inode) +{ + struct lua_lsm_object *llo = lua_lsm_inode(inode); + kvcache_dict_init(&llo->dict); + return 0; +} + +/** + * inode_alloc_security + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(inode_alloc_security, struct inode *, inode) +{ + *newinode(L) = inode; +} + +/** + * inode_free_security + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE1(inode_free_security, struct inode *, inode) +{ + *newinode(L) = inode; +} + +/** + * inode_free_security_rcu - postpone + */ +LUA_LSM_POSTPONE_DEFINE1(inode_free_security_rcu, void *, inode_security) +{ + struct lua_lsm_object *llo = lua_lsm_inode_rcu(inode_security); + kvcache_dict_free(&llo->dict); +} + +/** + * TODO: inode_free_security_rcu + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE1(inode_free_security_rcu, void *, inode_security) +{ + lua_pushnil(L); /* TODO: inode_security */ +} + +/** + * inode_init_security + * Default: -EOPNOTSUPP + * + * hooks result format: + * [none] : default value + * nil : default value + * true : 0 + * false : -EPERM + * false, errno : -errno + * nil, errno : -errno + * name, value : fill the xattr struct, XXX: name must not be freed + */ +LUA_LSM_INT_NAKED_DEFINE5(inode_init_security, struct inode *, inode, + struct inode *, dir, const struct qstr *, qstr, + struct xattr *, xattrs, int *, xattr_count) +{ + int top = lua_gettop(L); + int ret = LSM_RET_DEFAULT(inode_init_security); + int nres; + int status; + + *newinode(L) = inode; + dir ? *newinode(L) = dir : lua_pushnil(L); + qstr ? lua_pushlstring(L, qstr->name, qstr->len) : lua_pushnil(L); + + lua_pushvalue(L, -3 - 4); /* env */ + lua_setfenv(L, -3 - 6); /* restore thread.env */ + + status = lua_pcall(L, 3, LUA_MULTRET, -3 - 6); + if (status != 0) { + const char * __maybe_unused error = lua_tostring(L, -1); + __log_err("pcall: status = %d, top = %d, %s\n", + status, lua_gettop(L), error); + return ret; + } + + /* stack: [traceback, thread, env, _MODULES, _M, res1, ...] */ + nres = lua_gettop(L) - top + 1; + switch (nres) { + case 0: + break; + case 1: + if (lua_type(L, top) == LUA_TBOOLEAN) + ret = lua_toboolean(L, top) ? 0 : -EPERM; + break; + default: + if (!lua_toboolean(L, top)) { + int errno = lua_tointeger(L, top + 1); + if (errno > 0 && errno <= MAX_ERRNO) + ret = -errno; + } else if (lua_isstring(L, top) && lua_isstring(L, top + 1)) { + struct xattr *xattr; + const char *value; + size_t len; + + xattr = lsm_get_xattr_slot(xattrs, xattr_count); + if (xattr) { + value = lua_tolstring(L, top + 1, &len); + xattr->value = kmemdup(value, len, GFP_NOFS); + if (xattr->value == NULL) { + ret = -ENOMEM; + break; + } + xattr->value_len = len; + xattr->name = lua_tostring(L, top); + ret = 0; + } else { + /* TODO */ + } + } + break; + } + return ret; +} + +/** + * TODO: inode_init_security_anon + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(inode_init_security_anon, struct inode *, inode, + const struct qstr *, name, const struct inode *, context_inode) +{ + *newinode(L) = inode; + lua_pushnil(L); /* TODO: name */ + *(const struct inode **)newinode(L) = context_inode; +} + +/** + * inode_create + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(inode_create, struct inode *, dir, + struct dentry *, dentry, umode_t, mode) +{ + *newinode(L) = dir; + *newdentry(L) = dentry; + lua_pushinteger(L, (lua_Integer)mode); +} + +/** + * inode_post_create_tmpfile + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE2(inode_post_create_tmpfile, struct mnt_idmap *, idmap, + struct inode *, inode) +{ + *newmntidmap(L) = idmap; + *newinode(L) = inode; +} + +/** + * inode_link + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(inode_link, struct dentry *, old_dentry, + struct inode *, dir, struct dentry *, new_dentry) +{ + *newdentry(L) = old_dentry; + *newinode(L) = dir; + *newdentry(L) = new_dentry; +} + +/** + * inode_unlink + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(inode_unlink, struct inode *, dir, struct dentry *, dentry) +{ + *newinode(L) = dir; + *newdentry(L) = dentry; +} + +/** + * inode_symlink + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(inode_symlink, struct inode *, dir, + struct dentry *, dentry, const char *, old_name) +{ + *newinode(L) = dir; + *newdentry(L) = dentry; + lua_pushstring(L, old_name); +} + +/** + * inode_mkdir + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(inode_mkdir, struct inode *, dir, + struct dentry *, dentry, umode_t, mode) +{ + *newinode(L) = dir; + *newdentry(L) = dentry; + lua_pushinteger(L, (lua_Integer)mode); +} + +/** + * inode_rmdir + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(inode_rmdir, struct inode *, dir, struct dentry *, dentry) +{ + *newinode(L) = dir; + *newdentry(L) = dentry; +} + +/** + * inode_mknod + * Default: 0 + */ +LUA_LSM_INT_DEFINE4(inode_mknod, struct inode *, dir, struct dentry *, dentry, + umode_t, mode, dev_t, dev) +{ + *newinode(L) = dir; + *newdentry(L) = dentry; + lua_pushinteger(L, (lua_Integer)mode); + lua_pushinteger(L, (lua_Integer)dev); +} + +/** + * inode_rename + * Default: 0 + */ +LUA_LSM_INT_DEFINE4(inode_rename, + struct inode *, old_dir, struct dentry *, old_dentry, + struct inode *, new_dir, struct dentry *, new_dentry) +{ + *newinode(L) = old_dir; + *newdentry(L) = old_dentry; + *newinode(L) = new_dir; + *newdentry(L) = new_dentry; +} + +/** + * inode_readlink + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(inode_readlink, struct dentry *, dentry) +{ + *newdentry(L) = dentry; +} + +/** + * inode_follow_link + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(inode_follow_link, struct dentry *, dentry, + struct inode *, inode, bool, rcu) +{ + *newdentry(L) = dentry; + *newinode(L) = inode; + lua_pushboolean(L, (int)rcu); +} + +/** + * inode_permission + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(inode_permission, struct inode *, inode, int, mask) +{ + *newinode(L) = inode; + lua_pushinteger(L, (lua_Integer)mask); +} + +/** + * TODO: inode_setattr + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(inode_setattr, struct mnt_idmap *, idmap, + struct dentry *, dentry, struct iattr *, attr) +{ + *newmntidmap(L) = idmap; + *newdentry(L) = dentry; + lua_pushnil(L); /* TODO: attr */ +} + +/** + * inode_post_setattr + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE3(inode_post_setattr, struct mnt_idmap *, idmap, + struct dentry *, dentry, int, ia_valid) +{ + *newmntidmap(L) = idmap; + *newdentry(L) = dentry; + lua_pushinteger(L, (lua_Integer)ia_valid); +} + +/** + * inode_getattr + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(inode_getattr, const struct path *, path) +{ + *(const struct path **)newpath(L) = path; +} + +/** + * inode_xattr_skipcap + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(inode_xattr_skipcap, const char *, name) +{ + lua_pushstring(L, name); +} + +/** + * inode_setxattr + * Default: 0 + */ +LUA_LSM_INT_DEFINE6(inode_setxattr, struct mnt_idmap *, idmap, + struct dentry *, dentry, const char *, name, + const void *, value, size_t, size, int, flags) +{ + *newmntidmap(L) = idmap; + *newdentry(L) = dentry; + lua_pushstring(L, name); + lua_pushlstring(L, value, size); + lua_pushinteger(L, (lua_Integer)flags); +} + +/** + * inode_post_setxattr + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE5(inode_post_setxattr, struct dentry *, dentry, + const char *, name, const void *, value, + size_t, size, int, flags) +{ + *newdentry(L) = dentry; + lua_pushstring(L, name); + lua_pushlstring(L, value, size); + lua_pushinteger(L, (lua_Integer)flags); +} + +/** + * inode_getxattr + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(inode_getxattr, struct dentry *, dentry, const char *, name) +{ + *newdentry(L) = dentry; + lua_pushstring(L, name); +} + +/** + * inode_listxattr + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(inode_listxattr, struct dentry *, dentry) +{ + *newdentry(L) = dentry; +} + +/** + * inode_removexattr + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(inode_removexattr, struct mnt_idmap *, idmap, + struct dentry *, dentry, const char *, name) +{ + *newmntidmap(L) = idmap; + *newdentry(L) = dentry; + lua_pushstring(L, name); +} + +/** + * inode_post_removexattr + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE2(inode_post_removexattr, struct dentry *, dentry, + const char *, name) +{ + *newdentry(L) = dentry; + lua_pushstring(L, name); +} + +/** + * TODO: inode_file_setattr + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(inode_file_setattr, struct dentry *, dentry, + struct file_kattr *, fa) +{ + *newdentry(L) = dentry; + lua_pushnil(L); /* TODO: fa */ +} + +/** + * TODO: inode_file_getattr + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(inode_file_getattr, struct dentry *, dentry, + struct file_kattr *, fa) +{ + *newdentry(L) = dentry; + lua_pushnil(L); /* TODO: fa */ +} + +/** + * TODO: inode_set_acl + * Default: 0 + */ +LUA_LSM_INT_DEFINE4(inode_set_acl, struct mnt_idmap *, idmap, + struct dentry *, dentry, const char *, acl_name, + struct posix_acl *, kacl) +{ + *newmntidmap(L) = idmap; + *newdentry(L) = dentry; + lua_pushstring(L, acl_name); + lua_pushnil(L); /* TODO: kacl */ +} + +/** + * TODO: inode_post_set_acl + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE3(inode_post_set_acl, struct dentry *, dentry, + const char *, acl_name, struct posix_acl *, kacl) +{ + *newdentry(L) = dentry; + lua_pushstring(L, acl_name); + lua_pushnil(L); /* TODO: kacl */ +} + +/** + * inode_get_acl + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(inode_get_acl, struct mnt_idmap *, idmap, + struct dentry *, dentry, const char *, acl_name) +{ + *newmntidmap(L) = idmap; + *newdentry(L) = dentry; + lua_pushstring(L, acl_name); +} + +/** + * inode_remove_acl + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(inode_remove_acl, struct mnt_idmap *, idmap, + struct dentry *, dentry, const char *, acl_name) +{ + *newmntidmap(L) = idmap; + *newdentry(L) = dentry; + lua_pushstring(L, acl_name); +} + +/** + * inode_post_remove_acl + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE3(inode_post_remove_acl, struct mnt_idmap *, idmap, + struct dentry *, dentry, const char *, acl_name) +{ + *newmntidmap(L) = idmap; + *newdentry(L) = dentry; + lua_pushstring(L, acl_name); +} + +/** + * inode_need_killpriv + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(inode_need_killpriv, struct dentry *, dentry) +{ + *newdentry(L) = dentry; +} + +/** + * inode_killpriv + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(inode_killpriv, struct mnt_idmap *, idmap, + struct dentry *, dentry) +{ + *newmntidmap(L) = idmap; + *newdentry(L) = dentry; +} + +/** + * inode_getsecurity + * Default: -EOPNOTSUPP + */ +LUA_LSM_INT_NAKED_DEFINE5(inode_getsecurity, struct mnt_idmap *, idmap, + struct inode *, inode, const char *, name, + void **, buffer, bool, alloc) +{ + int top = lua_gettop(L); + int ret = LSM_RET_DEFAULT(inode_getsecurity); + int status; + + *newmntidmap(L) = idmap; + *newinode(L) = inode; + lua_pushstring(L, name); + + lua_pushvalue(L, -3 - 4); /* env */ + lua_setfenv(L, -3 - 6); /* restore thread.env */ + + status = lua_pcall(L, 3, LUA_MULTRET, -3 - 6); + if (status != 0) { + const char * __maybe_unused error = lua_tostring(L, -1); + __log_err("pcall: status = %d, top = %d, %s\n", + status, lua_gettop(L), error); + } else { + /* stack: [traceback, thread, env, _MODULES, _M, res1, ...] */ + int nres = lua_gettop(L) - top + 1; + int tt; + switch (nres) { + case 0: + break; + case 1: + tt = lua_type(L, top); + if (tt == LUA_TBOOLEAN) { + ret = lua_toboolean(L, top) ? 0 : -EPERM; + } else if (tt == LUA_TSTRING) { + size_t len; + const char *v = lua_tolstring(L, top, &len); + ret = (int)len; + if (alloc) { + *buffer = kmemdup(v, len, GFP_NOFS); + if (*buffer == NULL) + ret = -ENOMEM; + } + } + break; + default: + if (!lua_toboolean(L, top)) { + int errno = lua_tointeger(L, top + 1); + if (errno > 0 && errno <= MAX_ERRNO) + ret = -errno; + } + break; + } + } + return ret; +} + +/** + * inode_setsecurity + * Default: -EOPNOTSUPP + */ +LUA_LSM_INT_DEFINE5(inode_setsecurity, struct inode *, inode, + const char *, name, const void *, value, + size_t, size, int, flags) +{ + *newinode(L) = inode; + lua_pushstring(L, name); + lua_pushlstring(L, value, size); + lua_pushinteger(L, (lua_Integer)flags); +} + +/** + * inode_listsecurity + * Default: 0 + */ +LUA_LSM_INT_NAKED_DEFINE3(inode_listsecurity, struct inode *, inode, + char *, buffer, size_t, buffer_size) +{ + int top = lua_gettop(L); + int ret = LSM_RET_DEFAULT(inode_listsecurity); + int status; + + *newinode(L) = inode; + + lua_pushvalue(L, -1 - 4); /* env */ + lua_setfenv(L, -1 - 6); /* restore thread.env */ + + status = lua_pcall(L, 1, LUA_MULTRET, -1 - 6); + if (status != 0) { + const char * __maybe_unused error = lua_tostring(L, -1); + __log_err("pcall: status = %d, top = %d, %s\n", + status, lua_gettop(L), error); + } else { + /* stack: [traceback, thread, env, _MODULES, _M, res1, ...] */ + int nres = lua_gettop(L) - top + 1; + int tt; + switch (nres) { + case 0: + break; + case 1: + tt = lua_type(L, top); + if (tt == LUA_TBOOLEAN) { + ret = lua_toboolean(L, top) ? 0 : -EPERM; + } else if (tt == LUA_TSTRING) { + size_t len; + const char *v = lua_tolstring(L, top, &len); + ret = (int)len; + if (buffer != NULL && len <= buffer_size) + memcpy(buffer, v, buffer_size); + } + break; + default: + if (!lua_toboolean(L, top)) { + int errno = lua_tointeger(L, top + 1); + if (errno > 0 && errno <= MAX_ERRNO) + ret = -errno; + } + break; + } + } + return ret; +} + +/** + * TODO: inode_getlsmprop + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE2(inode_getlsmprop, struct inode *, inode, + struct lsm_prop *, prop) +{ + *newinode(L) = inode; + lua_pushnil(L); /* TODO: prop */ +} + +/** + * TODO: inode_copy_up + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(inode_copy_up, struct dentry *, src, struct cred **, new) +{ + *newdentry(L) = src; + lua_pushnil(L); /* TODO: new */ +} + +/** + * inode_copy_up_xattr + * Default: -EOPNOTSUPP + */ +LUA_LSM_INT_DEFINE2(inode_copy_up_xattr, struct dentry *, src, + const char *, name) +{ + *newdentry(L) = src; + lua_pushstring(L, name); +} + +/** + * TODO: inode_setintegrity + * Default: 0 + */ +LUA_LSM_INT_DEFINE4(inode_setintegrity, const struct inode *, inode, + enum lsm_integrity_type, type, + const void *, value, size_t, size) +{ + *(const struct inode **)newinode(L) = inode; + lua_pushnil(L); /* TODO: type */ + lua_pushnil(L); /* TODO: value */ + lua_pushnil(L); /* TODO: size */ +} + +/** + * TODO: kernfs_init_security + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(kernfs_init_security, struct kernfs_node *, kn_dir, + struct kernfs_node *, kn) +{ + lua_pushnil(L); /* TODO: kn_dir */ + lua_pushnil(L); /* TODO: kn */ +} + +/** + * file_permission + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(file_permission, struct file *, file, int, mask) +{ + *newfile(L) = file; + lua_pushinteger(L, (lua_Integer)mask); +} + +/** + * file_alloc_security - prepare + */ +LUA_LSM_PREPARE_DEFINE1(file_alloc_security, struct file *, file) +{ + struct lua_lsm_object *llo = lua_lsm_file(file); + kvcache_dict_init(&llo->dict); + return 0; +} + +/** + * file_alloc_security + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(file_alloc_security, struct file *, file) +{ + *newfile(L) = file; +} + +/** + * file_release + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE1(file_release, struct file *, file) +{ + *newfile(L) = file; +} + +/** + * file_free_security - postpone + */ +LUA_LSM_POSTPONE_DEFINE1(file_free_security, struct file *, file) +{ + struct lua_lsm_object *llo = lua_lsm_file(file); + kvcache_dict_free(&llo->dict); +} + +/** + * file_free_security + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE1(file_free_security, struct file *, file) +{ + *newfile(L) = file; +} + +/** + * file_ioctl + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(file_ioctl, struct file *, file, + unsigned int, cmd, unsigned long, arg) +{ + *newfile(L) = file; + lua_pushinteger(L, (lua_Integer)cmd); + lua_pushnumber(L, (lua_Number)arg); +} + +/** + * file_ioctl_compat + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(file_ioctl_compat, struct file *, file, + unsigned int, cmd, unsigned long, arg) +{ + *newfile(L) = file; + lua_pushinteger(L, (lua_Integer)cmd); + lua_pushnumber(L, (lua_Number)arg); +} + +/** + * mmap_addr + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(mmap_addr, unsigned long, addr) +{ + lua_pushnumber(L, (lua_Number)addr); +} + +/** + * mmap_file + * Default: 0 + */ +LUA_LSM_INT_DEFINE4(mmap_file, struct file *, file, unsigned long, reqprot, + unsigned long, prot, unsigned long, flags) +{ + *newfile(L) = file; + lua_pushnumber(L, (lua_Number)reqprot); + lua_pushnumber(L, (lua_Number)prot); + lua_pushnumber(L, (lua_Number)flags); +} + +/** + * TODO: file_mprotect + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(file_mprotect, struct vm_area_struct *, vma, + unsigned long, reqprot, unsigned long, prot) +{ + lua_pushnil(L); /* TODO: vma */ + lua_pushnumber(L, (lua_Number)reqprot); + lua_pushnumber(L, (lua_Number)prot); +} + +/** + * file_lock + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(file_lock, struct file *, file, unsigned int, cmd) +{ + *newfile(L) = file; + lua_pushinteger(L, (lua_Integer)cmd); +} + +/** + * file_fcntl + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(file_fcntl, struct file *, file, + unsigned int, cmd, unsigned long, arg) +{ + *newfile(L) = file; + lua_pushinteger(L, (lua_Integer)cmd); + lua_pushnumber(L, (lua_Number)arg); +} + +/** + * file_set_fowner + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE1(file_set_fowner, struct file *, file) +{ + *newfile(L) = file; +} + +/** + * TODO: file_send_sigiotask + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(file_send_sigiotask, struct task_struct *, tsk, + struct fown_struct *, fown, int, sig) +{ + *newtask(L) = tsk; + lua_pushnil(L); /* TODO: fown */ + lua_pushinteger(L, (lua_Integer)sig); +} + +/** + * file_receive + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(file_receive, struct file *, file) +{ + *newfile(L) = file; +} + +/** + * file_open + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(file_open, struct file *, file) +{ + *newfile(L) = file; +} + +/** + * file_post_open + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(file_post_open, struct file *, file, int, mask) +{ + static const struct cflag_opt opts[] = { + { "exec", MAY_EXEC }, + { "write", MAY_WRITE }, + { "read", MAY_READ }, + { "append", MAY_APPEND }, + { "access", MAY_ACCESS }, + { "open", MAY_OPEN }, + { "chdir", MAY_CHDIR }, + { "notblock", MAY_NOT_BLOCK }, + { NULL, 0 } + }; + static const unsigned int flags = MAY_EXEC | MAY_WRITE | MAY_READ | + MAY_APPEND | MAY_ACCESS | MAY_OPEN | MAY_CHDIR | MAY_NOT_BLOCK; + *newfile(L) = file; + table_fromopts(L, opts, flags, (unsigned int)mask); +} + +/** + * file_truncate + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(file_truncate, struct file *, file) +{ + *newfile(L) = file; +} + +/** + * task_alloc - prepare + */ +LUA_LSM_PREPARE_DEFINE2(task_alloc, struct task_struct *, task, + unsigned long, clone_flags) +{ + return lua_task_blob_init(task); +} + +/** + * task_alloc + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(task_alloc, struct task_struct *, task, + unsigned long, clone_flags) +{ + *newtask(L) = task; + lua_pushnumber(L, (lua_Number)clone_flags); +} + +/** + * task_free - postpone + */ +LUA_LSM_POSTPONE_DEFINE1(task_free, struct task_struct *, task) +{ + lua_task_blob_free(task); +} + +/** + * task_free + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE1(task_free, struct task_struct *, task) +{ + *newtask(L) = task; +} + +/** + * cred_alloc_blank - prepare + */ +LUA_LSM_PREPARE_DEFINE2(cred_alloc_blank, struct cred *, cred, gfp_t, gfp) +{ + struct lua_lsm_object *llo = lua_lsm_cred(cred); + kvcache_dict_init(&llo->dict); + return 0; +} + +/** + * cred_alloc_blank + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(cred_alloc_blank, struct cred *, cred, gfp_t, gfp) +{ + *newcred(L) = cred; + lua_pushinteger(L, (lua_Integer)gfp); +} + +/** + * cred_free - postpone + */ +LUA_LSM_POSTPONE_DEFINE1(cred_free, struct cred *, cred) +{ + struct lua_lsm_object *llo = lua_lsm_cred(cred); + kvcache_dict_free(&llo->dict); +} + +/** + * cred_free + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE1(cred_free, struct cred *, cred) +{ + *newcred(L) = cred; +} + +/** + * cred_prepare - prepare + */ +LUA_LSM_PREPARE_DEFINE3(cred_prepare, struct cred *, new, + const struct cred *, old, gfp_t, gfp) +{ + struct lua_lsm_object *llo = lua_lsm_cred(new); + kvcache_dict_init(&llo->dict); + return 0; +} + +/** + * cred_prepare + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(cred_prepare, struct cred *, new, + const struct cred *, old, gfp_t, gfp) +{ + *newcred(L) = new; + *(const struct cred **)newcred(L) = old; + lua_pushinteger(L, (lua_Integer)gfp); +} + +/** + * cred_transfer + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE2(cred_transfer, struct cred *, new, + const struct cred *, old) +{ + *newcred(L) = new; + *(const struct cred **)newcred(L) = old; +} + +/** + * TODO: cred_getsecid + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE2(cred_getsecid, const struct cred *, c, u32 *, secid) +{ + *(const struct cred **)newcred(L) = c; + lua_pushnil(L); /* TODO: secid */ +} + +/** + * TODO: cred_getlsmprop + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE2(cred_getlsmprop, const struct cred *, c, + struct lsm_prop *, prop) +{ + *(const struct cred **)newcred(L) = c; + lua_pushnil(L); /* TODO: prop */ +} + +/** + * kernel_act_as + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(kernel_act_as, struct cred *, new, u32, secid) +{ + *newcred(L) = new; + lua_pushinteger(L, (lua_Integer)secid); +} + +/** + * kernel_create_files_as + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(kernel_create_files_as, struct cred *, new, + struct inode *, inode) +{ + *newcred(L) = new; + *newinode(L) = inode; +} + +/** + * kernel_module_request + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(kernel_module_request, char *, kmod_name) +{ + lua_pushstring(L, (const char *)kmod_name); +} + +/** + * TODO: kernel_load_data + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(kernel_load_data, enum kernel_load_data_id, id, + bool, contents) +{ + lua_pushnil(L); /* TODO: id */ + lua_pushboolean(L, (int)contents); +} + +/** + * TODO: kernel_post_load_data + * Default: 0 + */ +LUA_LSM_INT_DEFINE4(kernel_post_load_data, char *, buf, loff_t, size, + enum kernel_load_data_id, id, char *, description) +{ + lua_pushstring(L, (const char *)buf); + lua_pushinteger(L, (lua_Integer)size); + lua_pushnil(L); /* TODO: id */ + lua_pushstring(L, (const char *)description); +} + +/** + * TODO: kernel_read_file + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(kernel_read_file, struct file *, file, + enum kernel_read_file_id, id, bool, contents) +{ + *newfile(L) = file; + lua_pushnil(L); /* TODO: id */ + lua_pushboolean(L, (int)contents); +} + +/** + * TODO: kernel_post_read_file + * Default: 0 + */ +LUA_LSM_INT_DEFINE4(kernel_post_read_file, struct file *, file, + char *, buf, loff_t, size, enum kernel_read_file_id, id) +{ + *newfile(L) = file; + lua_pushstring(L, (const char *)buf); + lua_pushinteger(L, (lua_Integer)size); + lua_pushnil(L); /* TODO: id */ +} + +static void build_lsm_setid_flags(lua_State *L, int flags) +{ + const char *s = NULL; + switch (flags) { + case LSM_SETID_ID: s = "lsm_setid_id"; break; + case LSM_SETID_RE: s = "lsm_setid_re"; break; + case LSM_SETID_RES: s = "lsm_setid_res"; break; + case LSM_SETID_FS: s = "lsm_setid_fs"; break; + } + lua_pushstring(L, s); +} + +/** + * task_fix_setuid + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(task_fix_setuid, struct cred *, new, + const struct cred *, old, int, flags) +{ + *newcred(L) = new; + *(const struct cred **)newcred(L) = old; + build_lsm_setid_flags(L, flags); +} + +/** + * task_fix_setgid + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(task_fix_setgid, struct cred *, new, + const struct cred *, old, int, flags) +{ + *newcred(L) = new; + *(const struct cred **)newcred(L) = old; + build_lsm_setid_flags(L, flags); +} + +/** + * task_fix_setgroups + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(task_fix_setgroups, struct cred *, new, + const struct cred *, old) +{ + *newcred(L) = new; + *(const struct cred **)newcred(L) = old; +} + +/** + * task_setpgid + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(task_setpgid, struct task_struct *, p, pid_t, pgid) +{ + *newtask(L) = p; + lua_pushinteger(L, (lua_Integer)pgid); +} + +/** + * task_getpgid + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(task_getpgid, struct task_struct *, p) +{ + *newtask(L) = p; +} + +/** + * task_getsid + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(task_getsid, struct task_struct *, p) +{ + *newtask(L) = p; +} + +/** + * TODO: current_getlsmprop_subj + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE1(current_getlsmprop_subj, struct lsm_prop *, prop) +{ + lua_pushnil(L); /* TODO: prop */ +} + +/** + * TODO: task_getlsmprop_obj + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE2(task_getlsmprop_obj, struct task_struct *, p, + struct lsm_prop *, prop) +{ + *newtask(L) = p; + lua_pushnil(L); /* TODO: prop */ +} + +/** + * task_setnice + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(task_setnice, struct task_struct *, p, int, nice) +{ + *newtask(L) = p; + lua_pushinteger(L, (lua_Integer)nice); +} + +/** + * task_setioprio + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(task_setioprio, struct task_struct *, p, int, ioprio) +{ + *newtask(L) = p; + lua_pushinteger(L, (lua_Integer)ioprio); +} + +/** + * task_getioprio + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(task_getioprio, struct task_struct *, p) +{ + *newtask(L) = p; +} + +/** + * task_prlimit + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(task_prlimit, const struct cred *, cred, + const struct cred *, tcred, unsigned int, flags) +{ + *(const struct cred **)newcred(L) = cred; + *(const struct cred **)newcred(L) = tcred; + lua_pushinteger(L, (lua_Integer)flags); +} + +/** + * TODO: task_setrlimit + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(task_setrlimit, struct task_struct *, p, + unsigned int, resource, struct rlimit *, new_rlim) +{ + *newtask(L) = p; + lua_pushinteger(L, (lua_Integer)resource); + lua_pushnil(L); /* TODO: new_rlim */ +} + +/** + * task_setscheduler + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(task_setscheduler, struct task_struct *, p) +{ + *newtask(L) = p; +} + +/** + * task_getscheduler + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(task_getscheduler, struct task_struct *, p) +{ + *newtask(L) = p; +} + +/** + * task_movememory + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(task_movememory, struct task_struct *, p) +{ + *newtask(L) = p; +} + +/** + * TODO: task_kill + * Default: 0 + */ +LUA_LSM_INT_DEFINE4(task_kill, struct task_struct *, p, + struct kernel_siginfo *, info, int, sig, + const struct cred *, cred) +{ + *newtask(L) = p; + lua_pushnil(L); /* TODO: info */ + lua_pushinteger(L, (lua_Integer)sig); + cred ? *(const struct cred **)newcred(L) = cred : lua_pushnil(L); +} + +/** + * task_prctl + * Default: -ENOSYS + */ +LUA_LSM_INT_DEFINE5(task_prctl, int, option, unsigned long, arg2, + unsigned long, arg3, unsigned long, arg4, unsigned long, arg5) +{ + lua_pushinteger(L, (lua_Integer)option); + lua_pushnumber(L, (lua_Number)arg2); + lua_pushnumber(L, (lua_Number)arg3); + lua_pushnumber(L, (lua_Number)arg4); + lua_pushnumber(L, (lua_Number)arg5); +} + +/** + * task_to_inode + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE2(task_to_inode, struct task_struct *, p, + struct inode *, inode) +{ + *newtask(L) = p; + *newinode(L) = inode; +} + +/** + * userns_create + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(userns_create, const struct cred *, cred) +{ + *(const struct cred **)newcred(L) = cred; +} + +/** + * ipc_permission + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(ipc_permission, struct kern_ipc_perm *, ipcp, short, flag) +{ + *newipc(L) = ipcp; + lua_pushinteger(L, (lua_Integer)flag); +} + +/** + * TODO: ipc_getlsmprop + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE2(ipc_getlsmprop, struct kern_ipc_perm *, ipcp, + struct lsm_prop *, prop) +{ + *newipc(L) = ipcp; + lua_pushnil(L); /* TODO: prop */ +} + +/** + * msg_msg_alloc_security - prepare + */ +LUA_LSM_PREPARE_DEFINE1(msg_msg_alloc_security, struct msg_msg *, msg) +{ + struct lua_lsm_object *llo = lua_lsm_msgmsg(msg); + kvcache_dict_init(&llo->dict); + return 0; +} + +/** + * msg_msg_alloc_security + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(msg_msg_alloc_security, struct msg_msg *, msg) +{ + *newmsgmsg(L) = msg; +} + +/** + * msg_msg_free_security - postpone + */ +LUA_LSM_POSTPONE_DEFINE1(msg_msg_free_security, struct msg_msg *, msg) +{ + struct lua_lsm_object *llo = lua_lsm_msgmsg(msg); + kvcache_dict_free(&llo->dict); +} + +/** + * msg_msg_free_security + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE1(msg_msg_free_security, struct msg_msg *, msg) +{ + *newmsgmsg(L) = msg; +} + +/** + * msg_queue_alloc_security - prepare + */ +LUA_LSM_PREPARE_DEFINE1(msg_queue_alloc_security, struct kern_ipc_perm *, perm) +{ + struct lua_lsm_object *llo = lua_lsm_ipc(perm); + kvcache_dict_init(&llo->dict); + return 0; +} + +/** + * msg_queue_alloc_security + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(msg_queue_alloc_security, struct kern_ipc_perm *, perm) +{ + *newipc(L) = perm; +} + +/** + * msg_queue_free_security - postpone + */ +LUA_LSM_POSTPONE_DEFINE1(msg_queue_free_security, struct kern_ipc_perm *, perm) +{ + struct lua_lsm_object *llo = lua_lsm_ipc(perm); + kvcache_dict_free(&llo->dict); +} + +/** + * msg_queue_free_security + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE1(msg_queue_free_security, struct kern_ipc_perm *, perm) +{ + *newipc(L) = perm; +} + +/** + * msg_queue_associate + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(msg_queue_associate, struct kern_ipc_perm *, perm, + int, msqflg) +{ + *newipc(L) = perm; + lua_pushinteger(L, (lua_Integer)msqflg); +} + +/** + * msg_queue_msgctl + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(msg_queue_msgctl, struct kern_ipc_perm *, perm, int, cmd) +{ + *newipc(L) = perm; + lua_pushinteger(L, (lua_Integer)cmd); +} + +/** + * msg_queue_msgsnd + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(msg_queue_msgsnd, struct kern_ipc_perm *, perm, + struct msg_msg *, msg, int, msqflg) +{ + *newipc(L) = perm; + *newmsgmsg(L) = msg; + lua_pushinteger(L, (lua_Integer)msqflg); +} + +/** + * TODO: msg_queue_msgrcv + * Default: 0 + */ +LUA_LSM_INT_DEFINE5(msg_queue_msgrcv, struct kern_ipc_perm *, perm, + struct msg_msg *, msg, struct task_struct *, target, + long, type, int, mode) +{ + *newipc(L) = perm; + *newmsgmsg(L) = msg; + *newtask(L) = target; + lua_pushnil(L); /* TODO: type */ + lua_pushinteger(L, (lua_Integer)mode); +} + +/** + * shm_alloc_security - prepare + */ +LUA_LSM_PREPARE_DEFINE1(shm_alloc_security, struct kern_ipc_perm *, perm) +{ + struct lua_lsm_object *llo = lua_lsm_ipc(perm); + kvcache_dict_init(&llo->dict); + return 0; +} + +/** + * shm_alloc_security + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(shm_alloc_security, struct kern_ipc_perm *, perm) +{ + *newipc(L) = perm; +} + +/** + * shm_free_security - postpone + */ +LUA_LSM_POSTPONE_DEFINE1(shm_free_security, struct kern_ipc_perm *, perm) +{ + struct lua_lsm_object *llo = lua_lsm_ipc(perm); + kvcache_dict_free(&llo->dict); +} + +/** + * shm_free_security + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE1(shm_free_security, struct kern_ipc_perm *, perm) +{ + *newipc(L) = perm; +} + +/** + * shm_associate + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(shm_associate, struct kern_ipc_perm *, perm, int, shmflg) +{ + *newipc(L) = perm; + lua_pushinteger(L, (lua_Integer)shmflg); +} + +/** + * shm_shmctl + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(shm_shmctl, struct kern_ipc_perm *, perm, int, cmd) +{ + *newipc(L) = perm; + lua_pushinteger(L, (lua_Integer)cmd); +} + +/** + * TODO: shm_shmat + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(shm_shmat, struct kern_ipc_perm *, perm, + char __user *, shmaddr, int, shmflg) +{ + *newipc(L) = perm; + lua_pushnil(L); /* TODO: shmaddr */ + lua_pushinteger(L, (lua_Integer)shmflg); +} + +/** + * sem_alloc_security - prepare + */ +LUA_LSM_PREPARE_DEFINE1(sem_alloc_security, struct kern_ipc_perm *, perm) +{ + struct lua_lsm_object *llo = lua_lsm_ipc(perm); + kvcache_dict_init(&llo->dict); + return 0; +} + +/** + * sem_alloc_security + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(sem_alloc_security, struct kern_ipc_perm *, perm) +{ + *newipc(L) = perm; +} + +/** + * sem_free_security - postpone + */ +LUA_LSM_POSTPONE_DEFINE1(sem_free_security, struct kern_ipc_perm *, perm) +{ + struct lua_lsm_object *llo = lua_lsm_ipc(perm); + kvcache_dict_free(&llo->dict); +} + +/** + * sem_free_security + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE1(sem_free_security, struct kern_ipc_perm *, perm) +{ + *newipc(L) = perm; +} + +/** + * sem_associate + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(sem_associate, struct kern_ipc_perm *, perm, int, semflg) +{ + *newipc(L) = perm; + lua_pushinteger(L, (lua_Integer)semflg); +} + +/** + * sem_semctl + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(sem_semctl, struct kern_ipc_perm *, perm, int, cmd) +{ + *newipc(L) = perm; + lua_pushinteger(L, (lua_Integer)cmd); +} + +/** + * TODO: sem_semop + * Default: 0 + */ +LUA_LSM_INT_DEFINE4(sem_semop, struct kern_ipc_perm *, perm, + struct sembuf *, sops, unsigned, nsops, int, alter) +{ + *newipc(L) = perm; + lua_pushnil(L); /* TODO: sops */ + lua_pushinteger(L, (lua_Integer)nsops); + lua_pushinteger(L, (lua_Integer)alter); +} + +/** + * netlink_send + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(netlink_send, struct sock *, sk, struct sk_buff *, skb) +{ + *newsock(L) = sk; + *newskb(L) = skb; +} + +/** + * d_instantiate + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE2(d_instantiate, struct dentry *, dentry, + struct inode *, inode) +{ + dentry ? *newdentry(L) = dentry : lua_pushnil(L); + inode ? *newinode(L) = inode : lua_pushnil(L); +} + +/** + * TODO: getselfattr + * Default: -EOPNOTSUPP + */ +LUA_LSM_INT_DEFINE4(getselfattr, unsigned int, attr, + struct lsm_ctx __user *, ctx, u32 *, size, u32, flags) +{ + lua_pushinteger(L, (lua_Integer)attr); + lua_pushnil(L); /* TODO: ctx */ + lua_pushnil(L); /* TODO: size */ + lua_pushinteger(L, (lua_Integer)flags); +} + +/** + * TODO: setselfattr + * Default: -EOPNOTSUPP + */ +LUA_LSM_INT_DEFINE4(setselfattr, unsigned int, attr, struct lsm_ctx *, ctx, + u32, size, u32, flags) +{ + lua_pushinteger(L, (lua_Integer)attr); + lua_pushnil(L); /* TODO: ctx */ + lua_pushinteger(L, (lua_Integer)size); + lua_pushinteger(L, (lua_Integer)flags); +} + +/** + * TODO: getprocattr + * Default: -EINVAL + */ +LUA_LSM_INT_DEFINE3(getprocattr, struct task_struct *, p, + const char *, name, char **, value) +{ + *newtask(L) = p; + lua_pushstring(L, name); + lua_pushnil(L); /* TODO: value */ +} + +/** + * setprocattr + * Default: -EINVAL + */ +LUA_LSM_INT_DEFINE3(setprocattr, const char *, name, + void *, value, size_t, size) +{ + lua_pushstring(L, name); + lua_pushlstring(L, value, size); +} + +/** + * ismaclabel + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(ismaclabel, const char *, name) +{ + lua_pushstring(L, name); +} + +/** + * TODO: secid_to_secctx + * Default: -EOPNOTSUPP + */ +LUA_LSM_INT_DEFINE2(secid_to_secctx, u32, secid, struct lsm_context *, cp) +{ + lua_pushinteger(L, (lua_Integer)secid); + lua_pushnil(L); /* TODO: cp */ +} + +/** + * TODO: lsmprop_to_secctx + * Default: -EOPNOTSUPP + */ +LUA_LSM_INT_DEFINE2(lsmprop_to_secctx, struct lsm_prop *, prop, + struct lsm_context *, cp) +{ + lua_pushnil(L); /* TODO: prop */ + lua_pushnil(L); /* TODO: cp */ +} + +/** + * TODO: secctx_to_secid + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(secctx_to_secid, const char *, secdata, + u32, seclen, u32 *, secid) +{ + lua_pushstring(L, secdata); + lua_pushinteger(L, (lua_Integer)seclen); + lua_pushnil(L); /* TODO: secid */ +} + +/** + * TODO: release_secctx + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE1(release_secctx, struct lsm_context *, cp) +{ + lua_pushnil(L); /* TODO: cp */ +} + +/** + * inode_invalidate_secctx + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE1(inode_invalidate_secctx, struct inode *, inode) +{ + *newinode(L) = inode; +} + +/** + * TODO: inode_notifysecctx + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(inode_notifysecctx, struct inode *, inode, + void *, ctx, u32, ctxlen) +{ + *newinode(L) = inode; + lua_pushnil(L); /* TODO: ctx */ + lua_pushinteger(L, (lua_Integer)ctxlen); +} + +/** + * TODO: inode_setsecctx + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(inode_setsecctx, struct dentry *, dentry, + void *, ctx, u32, ctxlen) +{ + *newdentry(L) = dentry; + lua_pushnil(L); /* TODO: ctx */ + lua_pushinteger(L, (lua_Integer)ctxlen); +} + +/** + * TODO: inode_getsecctx + * Default: -EOPNOTSUPP + */ +LUA_LSM_INT_DEFINE2(inode_getsecctx, struct inode *, inode, + struct lsm_context *, cp) +{ + *newinode(L) = inode; + lua_pushnil(L); /* TODO: cp */ +} + +#if defined(CONFIG_SECURITY) && defined(CONFIG_WATCH_QUEUE) + +/** + * TODO: post_notification + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(post_notification, const struct cred *, w_cred, + const struct cred *, cred, struct watch_notification *, n) +{ + *(const struct cred **)newcred(L) = w_cred; + *(const struct cred **)newcred(L) = cred; + lua_pushnil(L); /* TODO: n */ +} + +#endif /* CONFIG_SECURITY && CONFIG_WATCH_QUEUE */ + +#if defined(CONFIG_SECURITY) && defined(CONFIG_KEY_NOTIFICATIONS) + +/** + * watch_key + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(watch_key, struct key *, key) +{ + *newkey(L) = key; +} + +#endif /* CONFIG_SECURITY && CONFIG_KEY_NOTIFICATIONS */ + +#ifdef CONFIG_SECURITY_NETWORK + +/** + * unix_stream_connect + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(unix_stream_connect, struct sock *, sock, + struct sock *, other, struct sock *, newsk) +{ + *newsock(L) = sock; + *newsock(L) = other; + *newsock(L) = newsk; +} + +/** + * unix_may_send + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(unix_may_send, struct socket *, sock, + struct socket *, other) +{ + *newsocket(L) = sock; + *newsocket(L) = other; +} + +/** + * socket_create + * Default: 0 + */ +LUA_LSM_INT_DEFINE4(socket_create, int, family, int, type, + int, protocol, int, kern) +{ + lua_pushinteger(L, (lua_Integer)family); + lua_pushinteger(L, (lua_Integer)type); + lua_pushinteger(L, (lua_Integer)protocol); + lua_pushinteger(L, (lua_Integer)kern); +} + +/** + * socket_post_create + * Default: 0 + */ +LUA_LSM_INT_DEFINE5(socket_post_create, struct socket *, sock, + int, family, int, type, int, protocol, int, kern) +{ + *newsocket(L) = sock; + lua_pushinteger(L, (lua_Integer)family); + lua_pushinteger(L, (lua_Integer)type); + lua_pushinteger(L, (lua_Integer)protocol); + lua_pushinteger(L, (lua_Integer)kern); +} + +/** + * socket_socketpair + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(socket_socketpair, struct socket *, socka, + struct socket *, sockb) +{ + *newsocket(L) = socka; + *newsocket(L) = sockb; +} + +static int build_sockaddr(lua_State *L, struct sockaddr *address, int addrlen) +{ + if (addrlen < offsetofend(struct sockaddr, sa_family)) + return 0; + + switch (address->sa_family) { + case AF_INET: + if (addrlen < sizeof(struct sockaddr_in)) + return 0; + break; + case AF_INET6: + if (addrlen < SIN6_LEN_RFC2133) + return 0; + break; + } + *newsockaddr(L) = address; + return 1; +} + +/** + * socket_bind + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(socket_bind, struct socket *, sock, + struct sockaddr *, address, int, addrlen) +{ + *newsocket(L) = sock; + build_sockaddr(L, address, addrlen); +} + +/** + * socket_connect + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(socket_connect, struct socket *, sock, + struct sockaddr *, address, int, addrlen) +{ + *newsocket(L) = sock; + build_sockaddr(L, address, addrlen); +} + +/** + * socket_listen + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(socket_listen, struct socket *, sock, int, backlog) +{ + *newsocket(L) = sock; + lua_pushinteger(L, (lua_Integer)backlog); +} + +/** + * socket_accept + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(socket_accept, struct socket *, sock, + struct socket *, newsock) +{ + *newsocket(L) = sock; + *newsocket(L) = newsock; +} + +/** + * TODO: socket_sendmsg + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(socket_sendmsg, struct socket *, sock, + struct msghdr *, msg, int, size) +{ + *newsocket(L) = sock; + lua_pushnil(L); /* TODO: msg */ + lua_pushinteger(L, (lua_Integer)size); +} + +/** + * TODO: socket_recvmsg + * Default: 0 + */ +LUA_LSM_INT_DEFINE4(socket_recvmsg, struct socket *, sock, + struct msghdr *, msg, int, size, int, flags) +{ + *newsocket(L) = sock; + lua_pushnil(L); /* TODO: msg */ + lua_pushinteger(L, (lua_Integer)size); + lua_pushinteger(L, (lua_Integer)flags); +} + +/** + * socket_getsockname + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(socket_getsockname, struct socket *, sock) +{ + *newsocket(L) = sock; +} + +/** + * socket_getpeername + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(socket_getpeername, struct socket *, sock) +{ + *newsocket(L) = sock; +} + +/** + * socket_getsockopt + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(socket_getsockopt, struct socket *, sock, + int, level, int, optname) +{ + *newsocket(L) = sock; + lua_pushinteger(L, (lua_Integer)level); + lua_pushinteger(L, (lua_Integer)optname); +} + +/** + * socket_setsockopt + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(socket_setsockopt, struct socket *, sock, + int, level, int, optname) +{ + *newsocket(L) = sock; + lua_pushinteger(L, (lua_Integer)level); + lua_pushinteger(L, (lua_Integer)optname); +} + +/** + * socket_shutdown + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(socket_shutdown, struct socket *, sock, int, how) +{ + *newsocket(L) = sock; + lua_pushinteger(L, (lua_Integer)how); +} + +/** + * socket_sock_rcv_skb + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(socket_sock_rcv_skb, struct sock *, sk, + struct sk_buff *, skb) +{ + *newsock(L) = sk; + *newskb(L) = skb; +} + +/** + * TODO: socket_getpeersec_stream + * Default: -ENOPROTOOPT + */ +LUA_LSM_INT_DEFINE4(socket_getpeersec_stream, struct socket *, sock, + sockptr_t, optval, sockptr_t, optlen, unsigned int, len) +{ + *newsocket(L) = sock; + lua_pushnil(L); /* TODO: optval */ + lua_pushnil(L); /* TODO: optlen */ + lua_pushinteger(L, (lua_Integer)len); +} + +/** + * TODO: socket_getpeersec_dgram + * Default: -ENOPROTOOPT + */ +LUA_LSM_INT_DEFINE3(socket_getpeersec_dgram, struct socket *, sock, + struct sk_buff *, skb, u32 *, secid) +{ + *newsocket(L) = sock; + *newskb(L) = skb; + lua_pushnil(L); /* TODO: secid */ +} + +/** + * sk_alloc_security - prepare + */ +LUA_LSM_PREPARE_DEFINE3(sk_alloc_security, struct sock *, sk, + int, family, gfp_t, priority) +{ + struct lua_lsm_object *llo = lua_lsm_sock(sk); + kvcache_dict_init(&llo->dict); + return 0; +} + +/** + * sk_alloc_security + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(sk_alloc_security, struct sock *, sk, + int, family, gfp_t, priority) +{ + *newsock(L) = sk; + lua_pushinteger(L, (lua_Integer)family); + lua_pushinteger(L, (lua_Integer)priority); +} + +/** + * sk_free_security - postpone + */ +LUA_LSM_POSTPONE_DEFINE1(sk_free_security, struct sock *, sk) +{ + struct lua_lsm_object *llo = lua_lsm_sock(sk); + kvcache_dict_free(&llo->dict); +} + +/** + * sk_free_security + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE1(sk_free_security, struct sock *, sk) +{ + *newsock(L) = sk; +} + +/** + * sk_clone_security + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE2(sk_clone_security, const struct sock *, sk, + struct sock *, newsk) +{ + *(const struct sock **)newsock(L) = sk; + *newsock(L) = newsk; +} + +/** + * TODO: sk_getsecid + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE2(sk_getsecid, const struct sock *, sk, u32 *, secid) +{ + *(const struct sock **)newsock(L) = sk; + lua_pushnil(L); /* TODO: secid */ +} + +/** + * sock_graft + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE2(sock_graft, struct sock *, sk, struct socket *, parent) +{ + *newsock(L) = sk; + *newsocket(L) = parent; +} + +/** + * TODO: inet_conn_request + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(inet_conn_request, const struct sock *, sk, + struct sk_buff *, skb, struct request_sock *, req) +{ + *(const struct sock **)newsock(L) = sk; + *newskb(L) = skb; + lua_pushnil(L); /* TODO: req */ +} + +/** + * TODO: inet_csk_clone + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE2(inet_csk_clone, struct sock *, newsk, + const struct request_sock *, req) +{ + *newsock(L) = newsk; + lua_pushnil(L); /* TODO: req */ +} + +/** + * inet_conn_established + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE2(inet_conn_established, struct sock *, sk, + struct sk_buff *, skb) +{ + *newsock(L) = sk; + *newskb(L) = skb; +} + +/** + * secmark_relabel_packet + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(secmark_relabel_packet, u32, secid) +{ + lua_pushinteger(L, (lua_Integer)secid); +} + +/** + * secmark_refcount_inc + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE0(secmark_refcount_inc) +{ + return; +} + +/** + * secmark_refcount_dec + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE0(secmark_refcount_dec) +{ + return; +} + +/** + * TODO: req_classify_flow + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE2(req_classify_flow, const struct request_sock *, req, + struct flowi_common *, flic) +{ + lua_pushnil(L); /* TODO: req */ + lua_pushnil(L); /* TODO: flic */ +} + +/** + * TODO: tun_dev_alloc_security + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(tun_dev_alloc_security, void *, security) +{ + lua_pushnil(L); /* TODO: security */ +} + +/** + * tun_dev_create + * Default: 0 + */ +LUA_LSM_INT_DEFINE0(tun_dev_create) +{ + return; +} + +/** + * TODO: tun_dev_attach_queue + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(tun_dev_attach_queue, void *, security) +{ + lua_pushnil(L); /* TODO: security */ +} + +/** + * TODO: tun_dev_attach + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(tun_dev_attach, struct sock *, sk, void *, security) +{ + *newsock(L) = sk; + lua_pushnil(L); /* TODO: security */ +} + +/** + * TODO: tun_dev_open + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(tun_dev_open, void *, security) +{ + lua_pushnil(L); /* TODO: security */ +} + +/** + * TODO: sctp_assoc_request + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(sctp_assoc_request, struct sctp_association *, asoc, + struct sk_buff *, skb) +{ + lua_pushnil(L); /* TODO: asoc */ + *newskb(L) = skb; +} + +/** + * sctp_bind_connect + * Default: 0 + */ +LUA_LSM_INT_DEFINE4(sctp_bind_connect, struct sock *, sk, int, optname, + struct sockaddr *, address, int, addrlen) +{ + *newsock(L) = sk; + lua_pushinteger(L, (lua_Integer)optname); + build_sockaddr(L, address, addrlen); +} + +/** + * TODO: sctp_sk_clone + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE3(sctp_sk_clone, struct sctp_association *, asoc, + struct sock *, sk, struct sock *, newsk) +{ + lua_pushnil(L); /* TODO: asoc */ + *newsock(L) = sk; + *newsock(L) = newsk; +} + +/** + * TODO: sctp_assoc_established + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(sctp_assoc_established, struct sctp_association *, asoc, + struct sk_buff *, skb) +{ + lua_pushnil(L); /* TODO: asoc */ + *newskb(L) = skb; +} + +/** + * mptcp_add_subflow + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(mptcp_add_subflow, struct sock *, sk, struct sock *, ssk) +{ + *newsock(L) = sk; + *newsock(L) = ssk; +} + +#endif /* CONFIG_SECURITY_NETWORK */ + +#ifdef CONFIG_SECURITY_INFINIBAND + +/** + * TODO: ib_pkey_access + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(ib_pkey_access, void *, sec, u64, subnet_prefix, u16, pkey) +{ + lua_pushnil(L); /* TODO: sec */ + lua_pushnumber(L, (lua_Number)subnet_prefix); + lua_pushinteger(L, (lua_Integer)pkey); +} + +/** + * TODO: ib_endport_manage_subnet + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(ib_endport_manage_subnet, void *, sec, + const char *, dev_name, u8, port_num) +{ + lua_pushnil(L); /* TODO: sec */ + lua_pushstring(L, dev_name); + lua_pushinteger(L, (lua_Integer)port_num); +} + +/** + * TODO: ib_alloc_security + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(ib_alloc_security, void *, sec) +{ + lua_pushnil(L); /* TODO: sec */ +} + +#endif /* CONFIG_SECURITY_INFINIBAND */ + +#ifdef CONFIG_SECURITY_NETWORK_XFRM + +/** + * TODO: xfrm_policy_alloc_security + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(xfrm_policy_alloc_security, struct xfrm_sec_ctx **, ctxp, + struct xfrm_user_sec_ctx *, sec_ctx, gfp_t, gfp) +{ + lua_pushnil(L); /* TODO: ctxp */ + lua_pushnil(L); /* TODO: sec_ctx */ + lua_pushinteger(L, (lua_Integer)gfp); +} + +/** + * TODO: xfrm_policy_clone_security + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(xfrm_policy_clone_security, struct xfrm_sec_ctx *, old_ctx, + struct xfrm_sec_ctx **, new_ctx) +{ + lua_pushnil(L); /* TODO: old_ctx */ + lua_pushnil(L); /* TODO: new_ctx */ +} + +/** + * TODO: xfrm_policy_free_security + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE1(xfrm_policy_free_security, struct xfrm_sec_ctx *, ctx) +{ + lua_pushnil(L); /* TODO: ctx */ +} + +/** + * TODO: xfrm_policy_delete_security + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(xfrm_policy_delete_security, struct xfrm_sec_ctx *, ctx) +{ + lua_pushnil(L); /* TODO: ctx */ +} + +/** + * TODO: xfrm_state_alloc + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(xfrm_state_alloc, struct xfrm_state *, x, + struct xfrm_user_sec_ctx *, sec_ctx) +{ + lua_pushnil(L); /* TODO: x */ + lua_pushnil(L); /* TODO: sec_ctx */ +} + +/** + * TODO: xfrm_state_alloc_acquire + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(xfrm_state_alloc_acquire, struct xfrm_state *, x, + struct xfrm_sec_ctx *, polsec, u32, secid) +{ + lua_pushnil(L); /* TODO: x */ + lua_pushnil(L); /* TODO: polsec */ + lua_pushinteger(L, (lua_Integer)secid); +} + +/** + * TODO: xfrm_state_free_security + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE1(xfrm_state_free_security, struct xfrm_state *, x) +{ + lua_pushnil(L); /* TODO: x */ +} + +/** + * TODO: xfrm_state_delete_security + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(xfrm_state_delete_security, struct xfrm_state *, x) +{ + lua_pushnil(L); /* TODO: x */ +} + +/** + * TODO: xfrm_policy_lookup + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(xfrm_policy_lookup, struct xfrm_sec_ctx *, ctx, + u32, fl_secid) +{ + lua_pushnil(L); /* TODO: ctx */ + lua_pushinteger(L, (lua_Integer)fl_secid); +} + +/** + * TODO: xfrm_state_pol_flow_match + * Default: 1 + */ +LUA_LSM_INT_DEFINE3(xfrm_state_pol_flow_match, struct xfrm_state *, x, + struct xfrm_policy *, xp, const struct flowi_common *, flic) +{ + lua_pushnil(L); /* TODO: x */ + lua_pushnil(L); /* TODO: xp */ + lua_pushnil(L); /* TODO: flic */ +} + +/** + * TODO: xfrm_decode_session + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(xfrm_decode_session, struct sk_buff *, skb, u32 *, secid, + int, ckall) +{ + *newskb(L) = skb; + lua_pushnil(L); /* TODO: secid */ + lua_pushinteger(L, (lua_Integer)ckall); +} + +#endif /* CONFIG_SECURITY_NETWORK_XFRM */ + +#ifdef CONFIG_KEYS + +/** + * key_alloc + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(key_alloc, struct key *, key, const struct cred *, cred, + unsigned long, flags) +{ + *newkey(L) = key; + *(const struct cred **)newcred(L) = cred; + lua_pushnumber(L, (lua_Number)flags); +} + +/** + * TODO: key_permission + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(key_permission, key_ref_t, key_ref, + const struct cred *, cred, enum key_need_perm, need_perm) +{ + lua_pushnil(L); /* TODO: key_ref */ + *(const struct cred **)newcred(L) = cred; + lua_pushnil(L); /* TODO: need_perm */ +} + +/** + * TODO: key_getsecurity + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(key_getsecurity, struct key *, key, char **, buffer) +{ + *newkey(L) = key; + lua_pushnil(L); /* TODO: buffer */ +} + +/** + * key_post_create_or_update + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE6(key_post_create_or_update, struct key *, keyring, + struct key *, key, const void *, payload, size_t, payload_len, + unsigned long, flags, bool, create) +{ + *newkey(L) = keyring; + *newkey(L) = key; + lua_pushlstring(L, payload, payload_len); + lua_pushnumber(L, (lua_Number)flags); + lua_pushboolean(L, (int)create); +} + +#endif /* CONFIG_KEYS */ + +#ifdef CONFIG_AUDIT + +/** + * TODO: audit_rule_init + * Default: 0 + */ +LUA_LSM_INT_DEFINE5(audit_rule_init, u32, field, u32, op, char *, rulestr, + void **, lsmrule, gfp_t, gfp) +{ + lua_pushinteger(L, (lua_Integer)field); + lua_pushinteger(L, (lua_Integer)op); + lua_pushstring(L, (const char *)rulestr); + lua_pushnil(L); /* TODO: lsmrule */ + lua_pushinteger(L, (lua_Integer)gfp); +} + +/** + * TODO: audit_rule_known + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(audit_rule_known, struct audit_krule *, krule) +{ + lua_pushnil(L); /* TODO: krule */ +} + +/** + * TODO: audit_rule_match + * Default: 0 + */ +LUA_LSM_INT_DEFINE4(audit_rule_match, struct lsm_prop *, prop, u32, field, + u32, op, void *, lsmrule) +{ + lua_pushnil(L); /* TODO: prop */ + lua_pushinteger(L, (lua_Integer)field); + lua_pushinteger(L, (lua_Integer)op); + lua_pushnil(L); /* TODO: lsmrule */ +} + +/** + * TODO: audit_rule_free + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE1(audit_rule_free, void *, lsmrule) +{ + lua_pushnil(L); /* TODO: lsmrule */ +} + +#endif /* CONFIG_AUDIT */ + +#ifdef CONFIG_BPF_SYSCALL + +/** + * TODO: bpf + * Default: 0 + */ +LUA_LSM_INT_DEFINE4(bpf, int, cmd, union bpf_attr *, attr, unsigned int, size, + bool, kernel) +{ + lua_pushinteger(L, (lua_Integer)cmd); + lua_pushnil(L); /* TODO: attr */ + lua_pushinteger(L, (lua_Integer)size); + lua_pushboolean(L, kernel); +} + +/** + * TODO: bpf_map + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(bpf_map, struct bpf_map *, map, fmode_t, fmode) +{ + lua_pushnil(L); /* TODO: map */ + lua_pushinteger(L, (lua_Integer)fmode); +} + +/** + * TODO: bpf_prog + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(bpf_prog, struct bpf_prog *, prog) +{ + lua_pushnil(L); /* TODO: prog */ +} + +/** + * TODO: bpf_map_create + * Default: 0 + */ +LUA_LSM_INT_DEFINE4(bpf_map_create, struct bpf_map *, map, + union bpf_attr *, attr, struct bpf_token *, token, bool, kernel) +{ + lua_pushnil(L); /* TODO: map */ + lua_pushnil(L); /* TODO: attr */ + lua_pushnil(L); /* TODO: token */ + lua_pushboolean(L, kernel); +} + +/** + * TODO: bpf_map_free + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE1(bpf_map_free, struct bpf_map *, map) +{ + lua_pushnil(L); /* TODO: map */ +} + +/** + * TODO: bpf_prog_load + * Default: 0 + */ +LUA_LSM_INT_DEFINE4(bpf_prog_load, struct bpf_prog *, prog, + union bpf_attr *, attr, struct bpf_token *, token, bool, kernel) +{ + lua_pushnil(L); /* TODO: prog */ + lua_pushnil(L); /* TODO: attr */ + lua_pushnil(L); /* TODO: token */ + lua_pushboolean(L, kernel); +} + +/** + * TODO: bpf_prog_free + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE1(bpf_prog_free, struct bpf_prog *, prog) +{ + lua_pushnil(L); /* TODO: prog */ +} + +/** + * TODO: bpf_token_create + * Default: 0 + */ +LUA_LSM_INT_DEFINE3(bpf_token_create, struct bpf_token *, token, + union bpf_attr *, attr, const struct path *, path) +{ + lua_pushnil(L); /* TODO: token */ + lua_pushnil(L); /* TODO: attr */ + *(const struct path **)newpath(L) = path; +} + +/** + * TODO: bpf_token_free + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE1(bpf_token_free, struct bpf_token *, token) +{ + lua_pushnil(L); /* TODO: token */ +} + +/** + * TODO: bpf_token_cmd + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(bpf_token_cmd, const struct bpf_token *, token, + enum bpf_cmd, cmd) +{ + lua_pushnil(L); /* TODO: token */ + lua_pushnil(L); /* TODO: cmd */ +} + +/** + * TODO: bpf_token_capable + * Default: 0 + */ +LUA_LSM_INT_DEFINE2(bpf_token_capable, const struct bpf_token *, token, + int, cap) +{ + lua_pushnil(L); /* TODO: token */ + lua_pushinteger(L, (lua_Integer)cap); +} + +#endif /* CONFIG_BPF_SYSCALL */ + +/** + * TODO: locked_down + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(locked_down, enum lockdown_reason, what) +{ + lua_pushnil(L); /* TODO: what */ +} + +#ifdef CONFIG_PERF_EVENTS + +/** + * perf_event_open + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(perf_event_open, int, type) +{ + lua_pushinteger(L, (lua_Integer)type); +} + +/** + * perf_event_alloc + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(perf_event_alloc, struct perf_event *, event) +{ + *newperfevent(L) = event; +} + +/** + * perf_event_read + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(perf_event_read, struct perf_event *, event) +{ + *newperfevent(L) = event; +} + +/** + * perf_event_write + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(perf_event_write, struct perf_event *, event) +{ + *newperfevent(L) = event; +} + +#endif /* CONFIG_PERF_EVENTS */ + +#ifdef CONFIG_IO_URING + +/** + * uring_override_creds + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(uring_override_creds, const struct cred *, new) +{ + *(const struct cred **)newcred(L) = new; +} + +/** + * uring_sqpoll + * Default: 0 + */ +LUA_LSM_INT_DEFINE0(uring_sqpoll) +{ + return; +} + +/** + * TODO: uring_cmd + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(uring_cmd, struct io_uring_cmd *, ioucmd) +{ + lua_pushnil(L); /* TODO: ioucmd */ +} + +/** + * uring_allowed + * Default: 0 + */ +LUA_LSM_INT_DEFINE0(uring_allowed) +{ + return; +} + +#endif /* CONFIG_IO_URING */ + +/** + * initramfs_populated + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE0(initramfs_populated) +{ + return; +} + +/** + * bdev_alloc_security - prepare + */ +LUA_LSM_PREPARE_DEFINE1(bdev_alloc_security, struct block_device *, bdev) +{ + struct lua_lsm_object *llo = lua_lsm_bdev(bdev); + kvcache_dict_init(&llo->dict); + return 0; +} + +/** + * bdev_alloc_security + * Default: 0 + */ +LUA_LSM_INT_DEFINE1(bdev_alloc_security, struct block_device *, bdev) +{ + *newbdev(L) = bdev; +} + +/** + * bdev_free_security - postpone + */ +LUA_LSM_POSTPONE_DEFINE1(bdev_free_security, struct block_device *, bdev) +{ + struct lua_lsm_object *llo = lua_lsm_bdev(bdev); + kvcache_dict_free(&llo->dict); +} + +/** + * bdev_free_security + * Default: LSM_RET_VOID + */ +LUA_LSM_VOID_DEFINE1(bdev_free_security, struct block_device *, bdev) +{ + *newbdev(L) = bdev; +} + +/** + * TODO: bdev_setintegrity + * Default: 0 + */ +LUA_LSM_INT_DEFINE4(bdev_setintegrity, struct block_device *, bdev, + enum lsm_integrity_type, type, + const void *, value, size_t, size) +{ + *newbdev(L) = bdev; + lua_pushnil(L); /* TODO: type */ + lua_pushnil(L); /* TODO: value */ + lua_pushnil(L); /* TODO: size */ +} + From f5ee236e0da981a088268dafa8b8c3dc76a5f37d Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Tue, 26 Aug 2025 10:27:08 +0800 Subject: [PATCH 023/110] security: Add Lua based LSM This module leverages Lua's metaprogramming capabilities to develop complex access control logic and policies in Lua. Let us using a programmatic approach close to natural language, significantly lowers the barrier to entry for developing LSM access control. This module has the following notable features: * Supports developing LSM policies as mini-programs or LSM plugins. Multiple mini-programs or LSM plugins can be loaded simultaneously, and dynamic, on-demand uninstallation is supported. * Each mini-program runs in a secure sandbox environment. Script errors will not trigger kernel panics, and mini-programs are isolated from each other, ensuring no impact. * Mini-programs interact with the kernel through a specific, limited API, ensuring kernel security. * The LSM module features deep integration with the Lua language, enabling the most natural way to share data and set kernel object attributes. Signed-off-by: Tianjia Zhang Co-developed-by: Zongyao Chen --- include/linux/lsm_count.h | 9 +- include/uapi/linux/lsm.h | 1 + security/Kconfig | 11 +- security/Makefile | 1 + security/lua/Kconfig | 18 + security/lua/Makefile | 10 + security/lua/kvcache.c | 847 ++++++++++++++++++++++++++++++++++++++ security/lua/lsm.c | 835 +++++++++++++++++++++++++++++++++++++ security/lua/securityfs.c | 210 ++++++++++ 9 files changed, 1936 insertions(+), 6 deletions(-) create mode 100644 security/lua/Kconfig create mode 100644 security/lua/Makefile create mode 100644 security/lua/kvcache.c create mode 100644 security/lua/lsm.c create mode 100644 security/lua/securityfs.c diff --git a/include/linux/lsm_count.h b/include/linux/lsm_count.h index 16eb49761b25..f3d22f014cf6 100644 --- a/include/linux/lsm_count.h +++ b/include/linux/lsm_count.h @@ -102,6 +102,12 @@ #define IPE_ENABLED #endif +#if IS_ENABLED(CONFIG_SECURITY_LUA_LSM) +#define LUA_LSM_ENABLED 1, +#else +#define LUA_LSM_ENABLED +#endif + /* * There is a trailing comma that we need to be accounted for. This is done by * using a skipped argument in __COUNT_LSMS @@ -124,7 +130,8 @@ LANDLOCK_ENABLED \ IMA_ENABLED \ EVM_ENABLED \ - IPE_ENABLED) + IPE_ENABLED \ + LUA_LSM_ENABLED) #else diff --git a/include/uapi/linux/lsm.h b/include/uapi/linux/lsm.h index 938593dfd5da..2ac49b9cb1fd 100644 --- a/include/uapi/linux/lsm.h +++ b/include/uapi/linux/lsm.h @@ -65,6 +65,7 @@ struct lsm_ctx { #define LSM_ID_IMA 111 #define LSM_ID_EVM 112 #define LSM_ID_IPE 113 +#define LSM_ID_LUA 114 /* * LSM_ATTR_XXX definitions identify different LSM attributes diff --git a/security/Kconfig b/security/Kconfig index 285f284dfcac..31b2a06752a4 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -230,6 +230,7 @@ source "security/safesetid/Kconfig" source "security/lockdown/Kconfig" source "security/landlock/Kconfig" source "security/ipe/Kconfig" +source "security/lua/Kconfig" source "security/integrity/Kconfig" @@ -270,11 +271,11 @@ endchoice config LSM string "Ordered list of enabled LSMs" depends on SECURITY - default "landlock,lockdown,yama,loadpin,safesetid,smack,selinux,tomoyo,apparmor,ipe,bpf" if DEFAULT_SECURITY_SMACK - default "landlock,lockdown,yama,loadpin,safesetid,apparmor,selinux,smack,tomoyo,ipe,bpf" if DEFAULT_SECURITY_APPARMOR - default "landlock,lockdown,yama,loadpin,safesetid,tomoyo,ipe,bpf" if DEFAULT_SECURITY_TOMOYO - default "landlock,lockdown,yama,loadpin,safesetid,ipe,bpf" if DEFAULT_SECURITY_DAC - default "landlock,lockdown,yama,loadpin,safesetid,selinux,smack,tomoyo,apparmor,ipe,bpf" + default "landlock,lockdown,yama,loadpin,safesetid,lua,smack,selinux,tomoyo,apparmor,ipe,bpf" if DEFAULT_SECURITY_SMACK + default "landlock,lockdown,yama,loadpin,safesetid,lua,apparmor,selinux,smack,tomoyo,ipe,bpf" if DEFAULT_SECURITY_APPARMOR + default "landlock,lockdown,yama,loadpin,safesetid,lua,tomoyo,ipe,bpf" if DEFAULT_SECURITY_TOMOYO + default "landlock,lockdown,yama,loadpin,safesetid,lua,ipe,bpf" if DEFAULT_SECURITY_DAC + default "landlock,lockdown,yama,loadpin,safesetid,lua,selinux,smack,tomoyo,apparmor,ipe,bpf" help A comma-separated list of LSMs, in initialization order. Any LSMs left off this list, except for those with order diff --git a/security/Makefile b/security/Makefile index 4601230ba442..59d6f36e4611 100644 --- a/security/Makefile +++ b/security/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_CGROUPS) += device_cgroup.o obj-$(CONFIG_BPF_LSM) += bpf/ obj-$(CONFIG_SECURITY_LANDLOCK) += landlock/ obj-$(CONFIG_SECURITY_IPE) += ipe/ +obj-$(CONFIG_SECURITY_LUA_LSM) += lua/ # Object integrity file lists obj-$(CONFIG_INTEGRITY) += integrity/ diff --git a/security/lua/Kconfig b/security/lua/Kconfig new file mode 100644 index 000000000000..355666748758 --- /dev/null +++ b/security/lua/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +config SECURITY_LUA_LSM + bool "Lua based LSM Support" + depends on SECURITY + select LUA + default n + help + This selects the Lua based LSM. + If you are unsure how to answer this question, answer N. + +config SECURITY_LUA_LSM_STATS + bool "lua-lsm statistics" + depends on SECURITY_LUA_LSM + default n + help + This selects the lua-lsm statistics. + If you are unsure how to answer this question, answer N. diff --git a/security/lua/Makefile b/security/lua/Makefile new file mode 100644 index 000000000000..eec55de1ac0e --- /dev/null +++ b/security/lua/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Makefile for the Lua based LSM +# + +obj-$(CONFIG_SECURITY_LUA_LSM) := lua-lsm.o + +lua-lsm-y := lsm.o securityfs.o auxlib.o kvcache.o lsm_defs.o \ + lua_kernel.o lua_fs.o lua_net.o \ + lua_errno.o lua_capability.o lua_signal.o diff --git a/security/lua/kvcache.c b/security/lua/kvcache.c new file mode 100644 index 000000000000..965590a76f9b --- /dev/null +++ b/security/lua/kvcache.c @@ -0,0 +1,847 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Lua based LSM + * + * Copyright (C) 2025 The Alibaba Cloud Linux Authors. + */ + +#include "debug.h" +#include +#include +#include +#include +#include +#include +#include "auxlib.h" +#include "lsm.h" +#include "kvcache.h" + +static atomic_t node_nalloc = ATOMIC_INIT(0); +static atomic_t node_nfree = ATOMIC_INIT(0); + +static int kvcache_node_cmp(struct kvcache_node *n1, struct kvcache_node *n2) +{ + int n = strcmp(n1->key, n2->key); + if (n != 0) + return n; + return (char *)n1->module - (char *)n2->module; +} +RB_GENERATE_STATIC(kvcache, kvcache_node, node, kvcache_node_cmp); + + +static int kvcache_result(lua_State *L, int err, int nresults) +{ + const char *error = "unknown error"; + + if (err >= 0) + return nresults; + + switch (err) { + case -ENOMEM: error = "no memory"; break; + case -EINVAL: error = "invalid value"; break; + case -ERANGE: error = "no space"; break; + case -ENOENT: error = "no module"; break; + } + lua_pushnil(L); + lua_pushstring(L, error); + return 2; +} + +static struct kvcache_node * +kvcache_lookup(struct kvcache_dict *dict, + struct lua_module *module, const char *key) +{ + struct kvcache_node tmp; + tmp.key = key; + tmp.module = module; + return RB_FIND(kvcache, &dict->root, &tmp); +} + +static struct kvcache_node * +kvcache_node_alloc(struct kvcache_dict *dict, struct lua_module *module, + const char *key, size_t len) +{ + size_t l = sizeof(struct kvcache_node); + struct kvcache_node *node; + + l += key ? (len + 1) : 0; + node = kmalloc(l, GFP_KERNEL); + if (node == NULL) + return NULL; + + if (key) { + memcpy((char *)(node + 1), key, len); + ((char *)(node + 1))[len] = '\0'; /* ending 0 */ + node->key = (const char *)(node + 1); + } else { + node->key = NULL; + } + node->tt = LUA_TNIL; + node->module = module; + node->dict = dict; + atomic_inc(&node_nalloc); + return node; +} + +#define kvcache_value_alloc() kvcache_node_alloc(NULL, NULL, NULL, 0) + +static void kvcache_node_free(struct kvcache_node *node); +static int kvcache_node_fill(lua_State *L, int idx, struct kvcache_node *node); + +static void kvcache_node_clear(struct kvcache_node *node) +{ + struct kvcache_node *n, *tmp; + switch (node->tt) { + case LUA_TBOOLEAN: + case LUA_TNUMBER: + case LUA_TLIGHTUSERDATA: + break; + case LUA_TSTRING: + kfree(node->s.s); + break; + case LUA_TTABLE: + TAILQ_FOREACH_SAFE(n, &node->q.h, qlist, tmp) { + TAILQ_REMOVE(&node->q.h, n, qlist); + node->q.l -= 1; + kvcache_node_free(n); + } + WARN_ON(node->q.l != 0); + break; + } + node->tt = LUA_TNIL; +} + +static void kvcache_node_free(struct kvcache_node *node) +{ + if (node->module) { + unsigned long flags; + spin_lock_irqsave(&node->module->kvnodes_lock, flags); + TAILQ_REMOVE(&node->module->kvnodes, node, modlist); + spin_unlock_irqrestore(&node->module->kvnodes_lock, flags); + } + kvcache_node_clear(node); + atomic_inc(&node_nfree); + kfree(node); +} + +static void +kvcache_module_link(struct kvcache_dict *dict, struct lua_module *module, + struct kvcache_node *node) +{ + RB_INSERT(kvcache, &dict->root, node); + atomic_inc(&dict->count); + if (module) { + unsigned long flags; + spin_lock_irqsave(&module->kvnodes_lock, flags); + TAILQ_INSERT_TAIL(&module->kvnodes, node, modlist); + spin_unlock_irqrestore(&module->kvnodes_lock, flags); + } +} + +static void +kvcache_module_unlink(struct kvcache_dict *dict, struct lua_module *module, + struct kvcache_node *node) +{ + RB_REMOVE(kvcache, &dict->root, node); + atomic_dec(&dict->count); + if (module) { + unsigned long flags; + spin_lock_irqsave(&module->kvnodes_lock, flags); + TAILQ_REMOVE(&module->kvnodes, node, modlist); + spin_unlock_irqrestore(&module->kvnodes_lock, flags); + } +} + +static int +kvcache_qnode_new(lua_State *L, int idx, struct kvcache_node *head, int left) +{ + struct kvcache_node *node; + int err; + + node = kvcache_value_alloc(); + if (node == NULL) + return -ENOMEM; + + err = kvcache_node_fill(L, idx, node); + if (err) { + kvcache_node_free(node); + return err; + } + + if (left) + TAILQ_INSERT_HEAD(&head->q.h, node, qlist); + else + TAILQ_INSERT_TAIL(&head->q.h, node, qlist); + + head->q.l += 1; + return 0; +} + +static int kvcache_node_fill(lua_State *L, int idx, struct kvcache_node *node) +{ + const char *s; + int tidx; + int err = 0; + + node->tt = lua_type(L, idx); + switch (node->tt) { + case LUA_TBOOLEAN: + node->b = lua_toboolean(L, idx); + break; + + case LUA_TNUMBER: + node->n = lua_tonumber(L, idx); + break; + + case LUA_TLIGHTUSERDATA: + node->p = lua_touserdata(L, idx); + break; + + case LUA_TSTRING: + s = lua_tolstring(L, idx, &node->s.l); + node->s.s = kmalloc(node->s.l + 1, GFP_KERNEL); + if (node->s.s == NULL) + return -ENOMEM; + memcpy((void *)node->s.s, s, node->s.l + 1/* ending 0 */); + break; + + case LUA_TTABLE: + TAILQ_INIT(&node->q.h); + node->q.l = 0; + tidx = idx > 0 ? idx : idx - 1; + lua_pushnil(L); + while (lua_next(L, tidx) != 0) { + /* uses 'key' (at index -2) and 'value' (at index -1) */ + err = kvcache_qnode_new(L, -1, node, 0); + if (err) + break; + + /* removes 'value'; keeps 'key' for next iteration */ + lua_pop(L, 1); + } + if (err) { + kvcache_node_clear(node); + /* removes 'key' and 'value' */ + lua_pop(L, 2); + return err; + } + break; + + default: + return -EINVAL; + } + return 0; +} + +static int kvcache_node_refill(lua_State *L, int idx, struct kvcache_node *node) +{ + struct kvcache_node ntmp; + int err; + + err = kvcache_node_fill(L, idx, &ntmp); + if (err) + return err; + + kvcache_node_clear(node); + + node->tt = ntmp.tt; + switch (node->tt) { + case LUA_TBOOLEAN: + node->b = ntmp.b; + break; + case LUA_TNUMBER: + node->n = ntmp.n; + break; + case LUA_TLIGHTUSERDATA: + node->p = ntmp.p; + break; + case LUA_TSTRING: + node->s.s = ntmp.s.s; + node->s.l = ntmp.s.l; + break; + case LUA_TTABLE: + TAILQ_INIT(&node->q.h); + TAILQ_SWAP(&node->q.h, &ntmp.q.h, kvcache_node, qlist); + node->q.l = ntmp.q.l; + break; + } + return 0; +} + +static int +kvcache_set(lua_State *L, struct kvcache_dict *dict, struct lua_module *module) +{ + size_t len; + const char *key = luaL_checklstring(L, 2, &len); + int tt = lua_type(L, 3); + struct kvcache_node *node; + int err = 0; + + write_lock(&dict->lock); + node = kvcache_lookup(dict, module, key); + if (node) { + if (tt == LUA_TNIL) { + kvcache_module_unlink(dict, module, node); + kvcache_node_free(node); + } else { + err = kvcache_node_refill(L, 3, node); + } + goto unlock; + } + + if (tt == LUA_TNIL) + goto unlock; + + err = -ERANGE; + if (atomic_read(&dict->count) >= dict->capacity) + goto unlock; + + err = -ENOMEM; + node = kvcache_node_alloc(dict, module, key, len); + if (node == NULL) + goto unlock; + + err = kvcache_node_fill(L, 3, node); + if (err) { + kvcache_node_free(node); + goto unlock; + } + + kvcache_module_link(dict, module, node); + +unlock: + write_unlock(&dict->lock); + + if (!err) + lua_pushboolean(L, 1); + + return kvcache_result(L, err, 1); +} + +static int kvcache_node_get(lua_State *L, struct kvcache_node *node) +{ + struct kvcache_node *n; + int idx = 1; + + switch (node->tt) { + case LUA_TBOOLEAN: + lua_pushboolean(L, node->b); + break; + case LUA_TNUMBER: + lua_pushnumber(L, node->n); + break; + case LUA_TLIGHTUSERDATA: + lua_pushlightuserdata(L, node->p); + break; + case LUA_TSTRING: + lua_pushlstring(L, node->s.s, node->s.l); + break; + case LUA_TTABLE: + lua_createtable(L, node->q.l, 0); + TAILQ_FOREACH(n, &node->q.h, qlist) { + kvcache_node_get(L, n); + lua_rawseti(L, -2, idx++); + } + break; + default: + WARN_ON(1); + return -EINVAL; + } + return 0; +} + +static int +kvcache_get(lua_State *L, struct kvcache_dict *dict, struct lua_module *module) +{ + const char *key = luaL_checkstring(L, 2); + struct kvcache_node *node; + + read_lock(&dict->lock); + node = kvcache_lookup(dict, module, key); + if (node) + kvcache_node_get(L, node); + else + lua_pushnil(L); + read_unlock(&dict->lock); + return 1; +} + +static int +kvcache_incr(lua_State *L, struct kvcache_dict *dict, struct lua_module *module) +{ + size_t len; + const char *key = luaL_checklstring(L, 2, &len); + lua_Number n = luaL_optnumber(L, 3, 1); + struct kvcache_node *node; + int err = 0; + + write_lock(&dict->lock); + node = kvcache_lookup(dict, module, key); + if (node) { + if (node->tt == LUA_TNUMBER) { + node->n += n; + lua_pushnumber(L, node->n); + } else { + err = -EINVAL; + } + goto unlock; + } + + err = -ERANGE; + if (atomic_read(&dict->count) >= dict->capacity) + goto unlock; + + err = -ENOMEM; + node = kvcache_node_alloc(dict, module, key, len); + if (node == NULL) + goto unlock; + + node->n = n; + node->tt = LUA_TNUMBER; + kvcache_module_link(dict, module, node); + lua_pushnumber(L, node->n); + err = 0; + +unlock: + write_unlock(&dict->lock); + + return kvcache_result(L, err, 1); +} + +static int +kvcache_qnode_append(lua_State *L, int from, struct kvcache_node *head, int left) +{ + int top = lua_gettop(L); + int nres = 0; + int err; + + for ( ; from <= top; from++) { + err = kvcache_qnode_new(L, from, head, left); + if (err) { + __log_err("qnode_new: from = %d, err = %d\n", from, err); + break; + } + nres += 1; + } + return nres; +} + +static int kvcache_push(lua_State *L, struct kvcache_dict *dict, + struct lua_module *module, int left) +{ + size_t len; + const char *key = luaL_checklstring(L, 2, &len); + struct kvcache_node *node; + int nres; + int err = 0; + + write_lock(&dict->lock); + node = kvcache_lookup(dict, module, key); + if (node) { + if (node->tt == LUA_TTABLE) { + nres = kvcache_qnode_append(L, 3, node, left); + lua_pushinteger(L, nres); + } else { + err = -EINVAL; + } + goto unlock; + } + + err = -ERANGE; + if (atomic_read(&dict->count) >= dict->capacity) + goto unlock; + + err = -ENOMEM; + node = kvcache_node_alloc(dict, module, key, len); + if (node == NULL) + goto unlock; + + node->tt = LUA_TTABLE; + node->q.l = 0; + TAILQ_INIT(&node->q.h); + nres = kvcache_qnode_append(L, 3, node, left); + + kvcache_module_link(dict, module, node); + + lua_pushinteger(L, nres); + err = 0; + +unlock: + write_unlock(&dict->lock); + + return kvcache_result(L, err, 1); +} + +static int +kvcache_qnode_pop(lua_State *L, int npop, struct kvcache_node *head, int left) +{ + struct kvcache_node *node; + int nres = 0; + + while (head->q.l > 0 && npop-- > 0) { + WARN_ON(TAILQ_EMPTY(&head->q.h)); + + if (left) + node = TAILQ_FIRST(&head->q.h); + else + node = TAILQ_LAST_FAST(&head->q.h, kvcache_node, qlist); + + TAILQ_REMOVE(&head->q.h, node, qlist); + head->q.l -= 1; + kvcache_node_get(L, node); + kvcache_node_free(node); + nres += 1; + } + return nres; +} + +static int kvcache_pop(lua_State *L, struct kvcache_dict *dict, + struct lua_module *module, int left) +{ + const char *key = luaL_checkstring(L, 2); + int npop = luaL_optint(L, 3, 1); + struct kvcache_node *node; + int nres = 0; + int err = 0; + + write_lock(&dict->lock); + node = kvcache_lookup(dict, module, key); + if (node) { + if (node->tt == LUA_TTABLE) + nres = kvcache_qnode_pop(L, npop, node, left); + else + err = -EINVAL; + } + write_unlock(&dict->lock); + return kvcache_result(L, err, nres); +} + +static int kvcache_llen(lua_State *L, struct kvcache_dict *dict, + struct lua_module *module) +{ + const char *key = luaL_checkstring(L, 2); + struct kvcache_node *node; + int err = 0; + + read_lock(&dict->lock); + node = kvcache_lookup(dict, module, key); + if (node) { + if (node->tt == LUA_TTABLE) + lua_pushinteger(L, node->q.l); + else + err = -EINVAL; + } + read_unlock(&dict->lock); + return kvcache_result(L, err, 1); +} + +void kvcache_module_nodes_gc(struct lua_module *module) +{ + TAILQ_HEAD(, kvcache_node) cleanup_list; + struct kvcache_node *node, *tmp; + unsigned long flags; + + TAILQ_INIT(&cleanup_list); + spin_lock_irqsave(&module->kvnodes_lock, flags); + TAILQ_SWAP(&module->kvnodes, &cleanup_list, kvcache_node, modlist); + spin_unlock_irqrestore(&module->kvnodes_lock, flags); + + TAILQ_FOREACH_SAFE(node, &cleanup_list, modlist, tmp) { + struct kvcache_dict *dict = node->dict; + + WARN_ON(!dict); + write_lock(&dict->lock); + RB_REMOVE(kvcache, &dict->root, node); + write_unlock(&dict->lock); + atomic_dec(&dict->count); + + TAILQ_REMOVE(&cleanup_list, node, modlist); + kvcache_node_free(node); + } +} + +void kvcache_dict_free(struct kvcache_dict *dict) +{ + struct kvcache_node *node, *n; + + write_lock(&dict->lock); + RB_FOREACH_SAFE(node, kvcache, &dict->root, n) { + RB_REMOVE(kvcache, &dict->root, node); + kvcache_node_free(node); + atomic_dec(&dict->count); + } + WARN_ON(atomic_read(&dict->count) != 0); + write_unlock(&dict->lock); +} + +void kvcache_dict_init(struct kvcache_dict *dict) +{ + rwlock_init(&dict->lock); + RB_INIT(&dict->root); + atomic_set(&dict->count, 0); + dict->capacity = CACHE_CAPACITY; +} + +void kvcache_status(int *nalloc, int *nfree) +{ + if (nalloc) + *nalloc = atomic_read(&node_nalloc); + if (nfree) + *nfree = atomic_read(&node_nfree); +} + +/******************************** object cache *******************************/ + +const int _module_sentinel; + +static inline struct lua_module *module_from_object_fenv(lua_State *L, int idx) +{ + struct lua_module *module; + + lua_getfenv(L, 1); + lua_pushlightuserdata(L, MODULE_KEY); + lua_gettable(L, -2); + if (!lua_islightuserdata(L, -1)) { + const char * __maybe_unused key = luaL_checkstring(L, 2); + if (luaL_callmeta(L, 1, "__tostring")) { + __log_err("NO object.fenv: <%s> key = %s, top = %d [%s]\n", + lua_tostring(L, -1), key, lua_gettop(L), + luaL_typename(L, -1)); + lua_pop(L, 3); + } else { + __log_err("NO object.fenv: key = %s, top = %d\n", + key, lua_gettop(L)); + lua_pop(L, 2); + } + return NULL; + } + + module = lua_touserdata(L, -1); + lua_pop(L, 2); + return module; +} + +int lua_object_get(lua_State *L, struct kvcache_dict *dict) +{ + struct lua_module *module; + + module = module_from_object_fenv(L, 1); + if (module == NULL) + return 0; + return kvcache_get(L, dict, module); +} + +int lua_object_incr(lua_State *L, struct kvcache_dict *dict) +{ + struct lua_module *module; + + module = module_from_object_fenv(L, 1); + if (module == NULL) + return kvcache_result(L, -ENOENT, 0); + + return kvcache_incr(L, dict, module); +} + +int lua_object_lpush(lua_State *L, struct kvcache_dict *dict) +{ + struct lua_module *module; + + module = module_from_object_fenv(L, 1); + if (module == NULL) + return kvcache_result(L, -ENOENT, 0); + + return kvcache_push(L, dict, module, 1); +} + +int lua_object_rpush(lua_State *L, struct kvcache_dict *dict) +{ + struct lua_module *module; + + module = module_from_object_fenv(L, 1); + if (module == NULL) + return kvcache_result(L, -ENOENT, 0); + + return kvcache_push(L, dict, module, 0); +} + +int lua_object_lpop(lua_State *L, struct kvcache_dict *dict) +{ + struct lua_module *module; + + module = module_from_object_fenv(L, 1); + if (module == NULL) + return kvcache_result(L, -ENOENT, 0); + + return kvcache_pop(L, dict, module, 1); +} + +int lua_object_rpop(lua_State *L, struct kvcache_dict *dict) +{ + struct lua_module *module; + + module = module_from_object_fenv(L, 1); + if (module == NULL) + return kvcache_result(L, -ENOENT, 0); + + return kvcache_pop(L, dict, module, 0); +} + +int lua_object_llen(lua_State *L, struct kvcache_dict *dict) +{ + struct lua_module *module; + + module = module_from_object_fenv(L, 1); + if (module == NULL) + return kvcache_result(L, -ENOENT, 0); + + return kvcache_llen(L, dict, module); +} + +/* + * __index = function(object, key) + * local mt = getmetatable(object) + * if not mt then + * return nil + * end + * local v = mt[key] + * if v then + * return v + * end + * local module = fenv.MODULE_KEY + * return C.kvcache_get(L, object.kvcache, module, key) + * end + */ +int lua_object_index(lua_State *L, struct kvcache_dict *dict) +{ + if (lua_getmetatable(L, 1) == 0) { + __log_err("NO metatable for userdata\n"); + return 0; + } + lua_pushvalue(L, 2); + lua_rawget(L, -2); /* metatable[key] */ + if (!lua_isnoneornil(L, -1)) + return 1; + + return lua_object_get(L, dict); +} + +/* + * __newindex = function(object, key, v) + * local module = fenv.MODULE_KEY + * return C.kvcache_set(L, object.kvcache, module, key, v) + * end + */ +int lua_object_newindex(lua_State *L, struct kvcache_dict *dict) +{ + struct lua_module *module; + + module = module_from_object_fenv(L, 1); + if (module == NULL) + return kvcache_result(L, -ENOENT, 0); + return kvcache_set(L, dict, module); +} + +/******************************** shared dict ********************************/ + +static int shdict_set(lua_State *L) +{ + struct kvcache_dict *shdict = toshdict(L, 1); + return kvcache_set(L, shdict, NULL); +} + +static int shdict_get(lua_State *L) +{ + struct kvcache_dict *shdict = toshdict(L, 1); + return kvcache_get(L, shdict, NULL); +} + +static int shdict_incr(lua_State *L) +{ + struct kvcache_dict *shdict = toshdict(L, 1); + return kvcache_incr(L, shdict, NULL); +} + +static int shdict_lpush(lua_State *L) +{ + struct kvcache_dict *shdict = toshdict(L, 1); + return kvcache_push(L, shdict, NULL, 1); +} + +static int shdict_rpush(lua_State *L) +{ + struct kvcache_dict *shdict = toshdict(L, 1); + return kvcache_push(L, shdict, NULL, 0); +} + +static int shdict_lpop(lua_State *L) +{ + struct kvcache_dict *shdict = toshdict(L, 1); + return kvcache_pop(L, shdict, NULL, 1); +} + +static int shdict_rpop(lua_State *L) +{ + struct kvcache_dict *shdict = toshdict(L, 1); + return kvcache_pop(L, shdict, NULL, 0); +} + +static int shdict_llen(lua_State *L) +{ + struct kvcache_dict *shdict = toshdict(L, 1); + return kvcache_llen(L, shdict, NULL); +} + +static int shdict_index(lua_State *L) +{ + struct kvcache_dict *shdict = toshdict(L, 1); + + /* metatable[key] */ + if (lua_getmetatable(L, 1) == 0) { + __log_err("NO metatable for userdata\n"); + return 0; + } + lua_pushvalue(L, 2); + lua_rawget(L, -2); + if (!lua_isnoneornil(L, -1)) + return 1; + + return kvcache_get(L, shdict, NULL); +} + +static int shdict_tostring(lua_State *L) +{ + struct kvcache_dict *shdict = toshdict(L, 1); + read_lock(&shdict->lock); + lua_pushfstring(L, "shdict (%d / %d)", + atomic_read(&shdict->count), shdict->capacity); + read_unlock(&shdict->lock); + return 1; +} + +static int shdict_gc(lua_State *L) +{ + __log_info("shdict already freed by unregister\n"); + return 0; +} + +static const luaL_Reg shdict_meth[] = { + { "set", shdict_set }, + { "get", shdict_get }, + { "incr", shdict_incr }, + { "lpush", shdict_lpush }, + { "rpush", shdict_rpush }, + { "lpop", shdict_lpop }, + { "rpop", shdict_rpop }, + { "llen", shdict_llen }, + { "__index", shdict_index }, + { "__newindex", shdict_set }, + { "__tostring", shdict_tostring }, + { "__gc", shdict_gc }, + { NULL, NULL } +}; + +int shdict_init(lua_State *L) +{ + createmeta(L, METH_SHARED_DICT, shdict_meth, 1, 1); + return 0; +} diff --git a/security/lua/lsm.c b/security/lua/lsm.c new file mode 100644 index 000000000000..d3e2e9f755e4 --- /dev/null +++ b/security/lua/lsm.c @@ -0,0 +1,835 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Lua based LSM + * + * Copyright (C) 2025 The Alibaba Cloud Linux Authors. + */ + +#define pr_fmt(fmt) "lua-lsm: " fmt + +#include "debug.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for __MAP */ +#include /* for ktime_get */ +#include +#include +#include +#include +#include +#include "lsm.h" +#include "lua_object.h" +#include "lsm_defs.h" + + +#ifdef DEBUG + +bool debug = true; + +static int __init disable_debug(char *str) +{ + debug = false; + return 1; +} +__setup("lua.nodebug", disable_debug); + +#endif /* ! DEBUG */ + +/********************************* lsm hook *********************************/ + +struct lua_modules_head lsm_modules = TAILQ_HEAD_INITIALIZER(lsm_modules); +DEFINE_RWLOCK(modules_lock); + +struct lua_lsm_hook_stat lua_lsm_hook_stats[] = { + #define LSM_HOOK(RET, DEFAULT, NAME, ...) \ + { .name = #NAME, .nhooks = ATOMIC_INIT(0), }, + + #include + #undef LSM_HOOK + { NULL } +}; + +/* LSM weak funcs */ +#define LSM_HOOK(RET, DEFAULT, NAME, ...) \ + int __weak __prepare_ ## NAME(__VA_ARGS__) \ + { \ + return 0; \ + } \ + void __weak __postpone_ ## NAME(__VA_ARGS__) \ + { \ + } + +#include +#undef LSM_HOOK + +/********************************** Lua VM **********************************/ + +static int lua_shared_index(lua_State *L) +{ + const char *name = luaL_checkstring(L, 2); + struct lua_module_shdict *shdict; + struct lua_module *module; + + __log_info("READ shared table, [%s] %s\n", + luaL_typename(L, 2), lua_tostring(L, 2) ?: "(null)"); + + lua_pushlightuserdata(L, MODULE_KEY); + lua_gettable(L, LUA_ENVIRONINDEX); /* env.MODULE_KEY */ + if (!lua_islightuserdata(L, -1)) { + __log_err("NO fenv module\n"); + return 0; + } + module = lua_touserdata(L, -1); + + write_lock(&module->shdict_lock); + TAILQ_FOREACH(shdict, &module->shdict, list) { + if (strcmp(shdict->name, name) == 0) + break; + } + if (!shdict) { + shdict = kmalloc(sizeof(struct lua_module_shdict), GFP_NOFS); + if (shdict == NULL) + goto err_unlock; + shdict->name = kstrdup(name, GFP_NOFS); + if (shdict->name == NULL) + goto err_free; + kvcache_dict_init(&shdict->dict); + atomic_inc(&module->shdict_count); + TAILQ_INSERT_TAIL(&module->shdict, shdict, list); + } + write_unlock(&module->shdict_lock); + + /* shared[name] = shdict */ + lua_pushvalue(L, 2); + *newshdict(L) = &shdict->dict; + lua_rawset(L, 1); + + lua_settop(L, 2); + lua_rawget(L, 1); + return 1; + +err_free: + kfree(shdict); +err_unlock: + write_unlock(&module->shdict_lock); + return 0; +} + +static int lua_shared_newindex(lua_State *L) +{ + __log_err("Invalid operation: WRITE shared table, [%s] %s - [%s] %s\n", + luaL_typename(L, 2), lua_tostring(L, 2) ?: "(null)", + luaL_typename(L, 3), lua_tostring(L, 3) ?: "(null)"); + return 0; +} + +static int lua_module_fenv_newindex(lua_State *L) +{ + /* args: t, k, v */ + __log_warn("warning: set global variable, [%s] %s - [%s] %s\n", + luaL_typename(L, 2), lua_tostring(L, 2) ?: "(null)", + luaL_typename(L, 3), lua_tostring(L, 3) ?: "(null)"); + + /* XXX: t[k] = v, Warning it, but still perform assignment */ + lua_rawset(L, 1); + return 0; +} + +static int lua_module_load(lua_State *L, struct lua_module *module) +{ + int err; + + lua_newtable(L); /* env */ + lua_pushvalue(L, -1); + lua_replace(L, LUA_ENVIRONINDEX); + + /* Must be after lua_replace(LUA_ENVIRONINDEX) to ensure correct env */ + lua_pushlightuserdata(L, MODULE_KEY); + lua_pushlightuserdata(L, module); + lua_settable(L, -3); /* env.MODULE_KEY = module */ + *newtask(L) = current; + lua_setfield(L, -2, "current"); /* env.current = current */ + + lua_newtable(L); /* shared table */ + lua_createtable(L, 0, 2); /* shared metatable */ + lua_pushcfunction(L, lua_shared_index); + lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, lua_shared_newindex); + lua_setfield(L, -2, "__newindex"); + lua_setmetatable(L, -2); /* setmetatable(shared, mt) */ + lua_setfield(L, -2, "shared"); /* env.shared = shared */ + + /* setmetatable(env, { __index = _G, __newindex = func }) */ + lua_createtable(L, 0, 2); /* metatable */ + lua_pushvalue(L, LUA_GLOBALSINDEX); + lua_setfield(L, -2, "__index"); /* metatable.__index = _G */ + lua_pushcfunction(L, lua_module_fenv_newindex); + lua_setfield(L, -2, "__newindex"); /* metatable.__newindex = func */ + lua_setmetatable(L, -2); /* setmetatable(env, metatable) */ + + lua_pushcfunction(L, lua_traceback); + err = luaL_loadbuffer_wrap(L, module->chunk, + module->chunk_len, module->name); + if (err) { + lua_pop(L, 2); + return err; + } + + /* stack: [env, traceback, modfunc] */ + lua_pushvalue(L, -3); + lua_setfenv(L, -2); /* setfenv(modfunc, env) */ + + err = lua_pcall_wrap(L, 0, 1, -2); + if (err) { + lua_pop(L, 2); + return err; + } + + /* stack: [env, traceback, _M] */ + if (!lua_istable(L, -1)) { + __log_err("module <%s> is not a table: top = %d [%s]\n", + module->name, lua_gettop(L), luaL_typename(L, -1)); + lua_pop(L, 3); + return -EBADF; + } + + lua_remove(L, -2); /* remove traceback */ + lua_remove(L, -2); /* remove env */ + /* NO error, only _M is returned */ + return 0; +} + +static int lua_module_index(lua_State *L) +{ + const char *key = luaL_checkstring(L, 2); + struct lua_module *module; + int err; + + /* module queries are always run with a read lock */ + TAILQ_FOREACH(module, &lsm_modules, list) { + if (strcmp(module->name, key) != 0) + continue; + + err = lua_module_load(L, module); + if (err) { + __log_err("load: %s, err = %d, top = %d\n", + key, err, lua_gettop(L)); + return 0; + } + + lua_insert(L, -2); + lua_pushvalue(L, -2); + /* stack: [table, thunk, key, thunk] */ + lua_rawset(L, 1); /* table[key] = thunk */ + return 1; /* return the thunk */ + } + + __log_err("'%s' NOT found, top = %d\n", key, lua_gettop(L)); + return 0; +} + + +static const luaL_Reg builtinlibs[] = { + { "kernel", luaopen_kernel }, + { "fs", luaopen_fs }, + { "net", luaopen_net }, + { "errno", luaopen_errno }, + { "capability", luaopen_capability }, + { "signal", luaopen_signal }, + { NULL, NULL } +}; + +static void lualibs_openall(lua_State *L) +{ + const luaL_Reg *lib; + + /* TODO: loaded if needed */ + for (lib = builtinlibs; lib->func; lib++) { + luaL_requiref(L, lib->name, lib->func, 0); + lua_pop(L, 1); + } +} + +static int ll_require(lua_State *L) +{ + const char *modname = luaL_checkstring(L, 1); + luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 1); + lua_getfield(L, -1, modname); /* _LOADED[modname] */ + return 1; +} + + +static atomic_t vm_nalloc = ATOMIC_INIT(0); +static atomic_t vm_nfree = ATOMIC_INIT(0); +static atomic_t mem_nalloc = ATOMIC_INIT(0); +static atomic_t mem_nrealloc = ATOMIC_INIT(0); +static atomic_t mem_nfree = ATOMIC_INIT(0); + +static void *lvm_alloc(void *ud, void *ptr, size_t osize, size_t nsize) +{ + (void)ud; + (void)osize; + + if (nsize == 0) { + atomic_inc(&mem_nfree); + kfree(ptr); + return NULL; + } else { + if (ptr) + atomic_inc(&mem_nrealloc); + else + atomic_inc(&mem_nalloc); + return krealloc(ptr, nsize, GFP_KERNEL); + } +} + +static int lvm_panic(lua_State *L) +{ + (void)L; /* to avoid warnings */ + pr_err("PANIC: unprotected error in call to Lua API (%s), top = %d\n", + lua_tostring(L, -1), lua_gettop(L)); + return 0; +} + +static int lvm_pmain(lua_State *L) +{ + luaL_openlibs(L); + + /* open builtin libraries */ + lualibs_openall(L); + + /* shared dict init */ + shdict_init(L); + + lua_gc(L, LUA_GCRESTART, 0); + + /* _G._G = nil, remove global variable _G */ + lua_pushnil(L); + lua_setfield(L, LUA_GLOBALSINDEX, "_G"); + + lua_pushcfunction(L, ll_require); + lua_setglobal(L, "require"); + + /* build _MODULES table with metatable */ + lua_newtable(L); /* _MODULES table */ + lua_createtable(L, 0, 1); /* metatable */ + lua_pushcfunction(L, lua_module_index); /* TODO: pass it as args */ + lua_setfield(L, -2, "__index"); /* metatable.__index = func */ + /* setmetatable(_MODULES, metatable) */ + lua_setmetatable(L, -2); + lua_setfield(L, LUA_REGISTRYINDEX, "_MODULES"); + + /* return true */ + lua_pushboolean(L, 1); + return 1; +} + +static lua_State *lua_state_alloc(void) +{ + lua_State *L; + int status; + + L = lua_newstate(lvm_alloc, NULL); + if (L == NULL) + return ERR_PTR(-ENOMEM); + + lua_atpanic(L, lvm_panic); + lua_gc(L, LUA_GCSTOP, 0); + + lua_pushcfunction(L, lvm_pmain); + status = lua_pcall(L, 0, 1, 0); + if (status != 0) { + __log_err("pcall: status = %d, top = %d, %s\n", + status, lua_gettop(L), lua_tostring(L, -1)); + status = -ENOEXEC; + } else if (!lua_toboolean(L, -1) && lua_gettop(L) != 1) { + __log_err("lvm_pmain: top = %d, stack[top] = [%s]\n", + lua_gettop(L), luaL_typename(L, -1)); + status = -EFAULT; + } else { + lua_pop(L, 1); /* pop boolean result */ + status = 0; + } + + if (status != 0) { + lua_close(L); + return ERR_PTR(status); + } + + atomic_inc(&vm_nalloc); + return L; +} + +static void lua_state_free(lua_State *L) +{ + if (L) { + atomic_inc(&vm_nfree); + lua_close(L); + } +} + +/********************************** module **********************************/ + +static int lvm_writer(lua_State *L, const void *b, size_t size, void *B) +{ + (void)L; + luaL_addlstring((luaL_Buffer *)B, (const char *)b, size); + return 0; +} + +int lua_module_register(const char *code, size_t len) +{ + struct lua_module *module, *m; + lua_State *L; + luaL_Buffer B; + const char *chunk; + size_t chunk_len; + int i; + int status; + int err; + + L = lua_state_alloc(); + if (IS_ERR(L)) + return PTR_ERR(L); + + err = luaL_loadbuffer_wrap(L, code, len, ""); + if (err) + goto err_free_lua; + + /* dump function */ + luaL_checktype(L, -1, LUA_TFUNCTION); + luaL_buffinit(L, &B); + status = lua_dump(L, lvm_writer, &B); + if (status != 0) { + __log_err("dump: unable to dump the function\n"); + err = -EFAULT; + goto err_free_lua; + } + luaL_pushresult(&B); + chunk = lua_tolstring(L, -1, &chunk_len); + __log_info("compiled, source_len = %d, chunk_len = %d\n", + (int)len, (int)chunk_len); + + /* TODO: run in sandbox, record the `require` lua modules */ + lua_pushcfunction(L, lua_traceback); + lua_pushvalue(L, -3); + /* stack: [func, chunk, traceback, func] */ + err = lua_pcall_wrap(L, 0, LUA_MULTRET, -2); + if (err) + goto err_free_lua; + if (!lua_istable(L, -1)) { + __log_err("pcall: top = %d [%s]\n", + lua_gettop(L), luaL_typename(L, -1)); + err = -EBADF; + goto err_free_lua; + } + __log_info("lua module loaded, top = %d\n", lua_gettop(L)); + + err = -ENOMEM; + module = kzalloc(sizeof(*module), GFP_KERNEL); + if (module == NULL) + goto err_free_lua; + + __BITMAP_ZERO(&module->hookfuncs); + + /* traversal the result table */ + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + const char *key, *s; + + if (!lua_isstring(L, -2)) { + __log_err("module table index must be a string\n"); + lua_pop(L, 1); + continue; + } + + key = lua_tostring(L, -2); + if (strcmp(key, "name") == 0) { + if (lua_isstring(L, -1)) { + s = lua_tostring(L, -1); + module->name = kstrdup(s, GFP_KERNEL); + } else { + __log_err("'%s' value must be a string\n", key); + } + } else if (strcmp(key, "author") == 0) { + if (lua_isstring(L, -1)) { + s = lua_tostring(L, -1); + module->author = kstrdup(s, GFP_KERNEL); + } else { + __log_err("'%s' value must be a string\n", key); + } + } else if (strcmp(key, "description") == 0) { + if (lua_isstring(L, -1)) { + s = lua_tostring(L, -1); + module->description = kstrdup(s, GFP_KERNEL); + } else { + __log_err("'%s' value must be a string\n", key); + } + } else if (strcmp(key, "license") == 0) { + if (lua_isstring(L, -1)) { + s = lua_tostring(L, -1); + module->license = kstrdup(s, GFP_KERNEL); + } else { + __log_err("'%s' value must be a string\n", key); + } + } else if (strcmp(key, "version") == 0) { + if (lua_isnumber(L, -1)) { + module->version = (int)lua_tointeger(L, -1); + } else { + __log_err("'%s' value must be a integer\n", key); + } + } else { + for (i = 0; lua_lsm_hook_stats[i].name; i++) { + if (strcmp(lua_lsm_hook_stats[i].name, key) != 0) + continue; + + if (!lua_isfunction(L, -1)) { + __log_err("'%s' field must be a function\n", key); + continue; + } + + module->nhooks += 1; + __BITMAP_SET(i, &module->hookfuncs); + + __log_info("hookfunc = %s\n", key); + break; + } + + if (lua_lsm_hook_stats[i].name == NULL) + __log_warn("'%s' is unknown function\n", key); + } + + /* removes 'value'; keeps 'key' for next iteration */ + lua_pop(L, 1); + } + + err = -ENOMEM; + if (module->name == NULL) + goto err_free_module; + + module->chunk = kmalloc(chunk_len, GFP_KERNEL); + if (module->chunk == NULL) + goto err_free_module; + memcpy(module->chunk, chunk, chunk_len); + module->chunk_len = chunk_len; + + TAILQ_INIT(&module->shdict); + rwlock_init(&module->shdict_lock); + atomic_set(&module->shdict_count, 0); + + TAILQ_INIT(&module->kvnodes); + spin_lock_init(&module->kvnodes_lock); + + write_lock(&modules_lock); + TAILQ_FOREACH(m, &lsm_modules, list) { + if (strcmp(module->name, m->name) == 0) + break; + } + if (m == NULL) { + TAILQ_INSERT_TAIL(&lsm_modules, module, list); + + for (i = 0; lua_lsm_hook_stats[i].name; i++) { + if (__BITMAP_ISSET(i, &module->hookfuncs)) + atomic_inc(&lua_lsm_hook_stats[i].nhooks); + } + } + write_unlock(&modules_lock); + + err = -EEXIST; + if (m != NULL) { + __log_err("module <%s> registered already\n", module->name); + goto err_free_module; + } + + __log_info("module <%s> registered with %d filters\n", + module->name, module->nhooks); + + lua_state_free(L); + return 0; + +err_free_module: + kfree(module->chunk); + kfree(module->name); + kfree(module->author); + kfree(module->description); + kfree(module->license); + kfree(module); +err_free_lua: + lua_state_free(L); + + return err; +} + +static int lua_module_remove(lua_State *L, struct lua_module *module) +{ + int removed = 0; + + /* registry._MODULES[modname] = nil */ + lua_getfield(L, LUA_REGISTRYINDEX, "_MODULES"); + if (lua_istable(L, -1)) { + lua_pushstring(L, module->name); + lua_pushnil(L); + lua_rawset(L, -3); + removed = 1; + } + lua_pop(L, 1); + return removed; +} + +int lua_module_unregister(const char *name) +{ + struct lua_module *module, *tmp; + struct lua_module_shdict *shdict, *shdict_tmp; + struct task_struct *g, *task; + unsigned int cpu; + int count = 0; + int i; + + write_lock_bh(&modules_lock); + TAILQ_FOREACH_SAFE(module, &lsm_modules, list, tmp) { + if (strcmp(module->name, name) == 0) { + TAILQ_REMOVE(&lsm_modules, module, list); + break; + } + } + + if (!module) { + write_unlock_bh(&modules_lock); + return -ENOENT; + } + + for (i = 0; lua_lsm_hook_stats[i].name; i++) { + if (__BITMAP_ISSET(i, &module->hookfuncs)) + atomic_dec(&lua_lsm_hook_stats[i].nhooks); + } + + TAILQ_FOREACH_SAFE(shdict, &module->shdict, list, shdict_tmp) { + TAILQ_REMOVE(&module->shdict, shdict, list); + kvcache_dict_free(&shdict->dict); + kfree(shdict->name); + kfree(shdict); + } + + kvcache_module_nodes_gc(module); + + /* remove loaded module from every Lua VM */ + read_lock(&tasklist_lock); + for_each_process_thread(g, task) { + lua_State *L = lua_lsm_task(task)->L; + if (lua_module_remove(L, module)) + count++; + } + read_unlock(&tasklist_lock); + + /* ditto for the idle 'swapper' tasks */ + cpus_read_lock(); + for_each_possible_cpu(cpu) { + task = idle_task(cpu); + if (lua_module_remove(lua_lsm_task(task)->L, module)) + count++; + } + cpus_read_unlock(); + + write_unlock_bh(&modules_lock); + + __log_info("unregistered module <%s> from %d/%d Lua VMs.\n", + name, count, atomic_read(&vm_nalloc) - atomic_read(&vm_nfree)); + + kfree(module->chunk); + kfree(module->name); + kfree(module->author); + kfree(module->description); + kfree(module->license); + kfree(module); + return 0; +} + +/*****************************************************************************/ + +int modules_show(struct seq_file *m, void *v) +{ + struct lua_module *module; + + seq_printf(m, "modules for lua-lsm\n"); + seq_printf(m, "%-12s %-10s %6s %6s %-48s\n", + "name", "license", "size", "nhooks", "author"); + seq_printf(m, "%s\n", TABLINE); + + read_lock(&modules_lock); + TAILQ_FOREACH(module, &lsm_modules, list) { + seq_printf(m, "%-12s %-10s %6zu %6d %-48s\n", + module->name, module->license, module->chunk_len, + module->nhooks, module->author); + } + read_unlock(&modules_lock); + return 0; +} + + +#ifdef CONFIG_SECURITY_LUA_LSM_STATS + +int lua_lsm_status_show(struct seq_file *m, void *v) +{ + struct task_struct *g, *p; + unsigned int cpu; + int nalloc, nfree; + int inuse, total, minimum, maximum, avg; + int nbytes; + lua_State *L; + + nalloc = atomic_read(&vm_nalloc); + nfree = atomic_read(&vm_nfree); + seq_printf(m, "Lua VM:\n"); + seq_printf(m, " Alloc: alloc %d, free %d, inuse %d\n", + nalloc, nfree, nalloc - nfree); + seq_printf(m, " Mem alloc: alloc %d, realloc %d, free %d\n", + atomic_read(&mem_nalloc), atomic_read(&mem_nrealloc), + atomic_read(&mem_nfree)); + + inuse = 0; + total = 0; + minimum = INT_MAX; + maximum = 0; + write_lock_bh(&modules_lock); + read_lock(&tasklist_lock); + for_each_process_thread(g, p) { + L = lua_lsm_task(p)->L; + nbytes = lua_gc(L, LUA_GCCOUNT, 0) * 1024 + lua_gc(L, LUA_GCCOUNTB, 0); + inuse++; + total += nbytes; + minimum = min(minimum, nbytes); + maximum = max(maximum, nbytes); + } + read_unlock(&tasklist_lock); + + cpus_read_lock(); + for_each_possible_cpu(cpu) { + L = lua_lsm_task(idle_task(cpu))->L; + nbytes = lua_gc(L, LUA_GCCOUNT, 0) * 1024 + lua_gc(L, LUA_GCCOUNTB, 0); + inuse++; + total += nbytes; + minimum = min(minimum, nbytes); + maximum = max(maximum, nbytes); + } + cpus_read_unlock(); + write_unlock_bh(&modules_lock); + + avg = inuse ? total / inuse : 0; + seq_printf(m, " Mem usage: total %d, min %d, max %d, inuse %d, average %d\n", + total, minimum, maximum, inuse, avg); + + kvcache_status(&nalloc, &nfree); + seq_printf(m, "kvcache:\n"); + seq_printf(m, " Alloc: alloc %d, free %d, inuse %d\n", + nalloc, nfree, nalloc - nfree); + + return 0; +} + +int lsmhook_stat_show(struct seq_file *m, void *v) +{ + struct lua_lsm_hook_stat *stat; + int i = 1; + + seq_printf(m, "stats for lua-lsm (ns)\n"); + seq_printf(m, "%3s %-28s %6s %8s %15s %10s %12s\n", + "num", "name", "nhooks", "count", "total", "average", "maxtime"); + seq_printf(m, "%s\n", TABLINE); + + for (stat = lua_lsm_hook_stats; stat->name; stat++) { + int n = atomic_read(&stat->count); + s64 total = atomic64_read(&stat->time); + s64 maxtime = atomic64_read(&stat->maxtime); + seq_printf(m, "%3d %-28s %6d %8d %15llu %10llu %12llu\n", + i++, stat->name, atomic_read(&stat->nhooks), + n, total, n ? total / n : 0, maxtime); + } + return 0; +} + +#endif + +/*********************************** main ***********************************/ + +int lua_task_blob_init(struct task_struct *task) +{ + struct lua_lsm_task *llt = lua_lsm_task(task); + lua_State *L; + + kvcache_dict_init(&llt->dict); + + L = lua_state_alloc(); + if (IS_ERR(L)) + return PTR_ERR(L); + + llt->L = L; + return 0; +} + +void lua_task_blob_free(struct task_struct *task) +{ + struct lua_lsm_task *llt = lua_lsm_task(task); + lua_state_free(llt->L); + llt->L = NULL; + kvcache_dict_free(&llt->dict); +} + +struct lsm_blob_sizes lua_lsm_blob_sizes __ro_after_init = { + .lbs_task = sizeof(struct lua_lsm_task), + .lbs_cred = sizeof(struct lua_lsm_object), + .lbs_file = sizeof(struct lua_lsm_object), + .lbs_ib = sizeof(struct lua_lsm_object), + .lbs_inode = sizeof(struct lua_lsm_object), + .lbs_sock = sizeof(struct lua_lsm_object), + .lbs_superblock = sizeof(struct lua_lsm_object), + .lbs_ipc = sizeof(struct lua_lsm_object), + .lbs_key = sizeof(struct lua_lsm_object), + .lbs_msg_msg = sizeof(struct lua_lsm_object), + .lbs_perf_event = sizeof(struct lua_lsm_object), + /* TODO: number of xattr slots in new_xattrs array */ + .lbs_xattr_count = 10, + .lbs_tun_dev = sizeof(struct lua_lsm_object), + .lbs_bdev = sizeof(struct lua_lsm_object), +}; + + +static struct security_hook_list lua_lsm_hooks[] __ro_after_init = { + #define LSM_HOOK(RET, DEFAULT, NAME, ...) \ + LSM_HOOK_INIT(NAME, lua_lsm_ ## NAME), + + #include + #undef LSM_HOOK +}; + +int lua_enabled __ro_after_init = 1; + +static const struct lsm_id lua_lsmid = { + .name = "lua", + .id = LSM_ID_LUA, +}; + +static int __init lua_lsm_init(void) +{ + int err; + + err = lua_task_blob_init(current); + if (err) + return err; + + security_add_hooks(lua_lsm_hooks, ARRAY_SIZE(lua_lsm_hooks), &lua_lsmid); + pr_info("Lua based LSM initialized\n"); + return 0; +} + +DEFINE_LSM(lua) = { + .name = "lua", + .enabled = &lua_enabled, + .blobs = &lua_lsm_blob_sizes, + .init = lua_lsm_init, +}; diff --git a/security/lua/securityfs.c b/security/lua/securityfs.c new file mode 100644 index 000000000000..1b83517b95ef --- /dev/null +++ b/security/lua/securityfs.c @@ -0,0 +1,210 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Lua based LSM + * + * Copyright (C) 2025 The Alibaba Cloud Linux Authors. + */ + +#include "debug.h" +#include +#include +#include "lsm.h" + + +static bool lua_capable(int cap) +{ + bool allow = true; + int rc; + + /* + * All kernel tasks are privileged + */ + if (unlikely(current->flags & PF_KTHREAD)) + return true; + + rc = cap_capable(current_cred(), &init_user_ns, cap, CAP_OPT_NONE); + if (rc < 0) { + allow = false; + __log_info("NO capability: cap = %d, allow=%d\n", cap, allow); + } + + return allow; +} + +static int version_show(struct seq_file *m, void *v) +{ + seq_printf(m, "%u", LUA_LSM_VERSION); + return 0; +} + +static int open_version(struct inode *inode, struct file *filp) +{ + return single_open(filp, version_show, NULL); +} + +static const struct file_operations fops_version = { + .open = open_version, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + + +static ssize_t register_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + char *buffer; + int err; + + __log_info("len = %d, %pD\n", (int)len, file); + + if (!lua_capable(CAP_MAC_ADMIN)) + return -EPERM; + + /* No partial writes. */ + if (*ppos != 0) + return -EINVAL; + if (len == 0) + return -EINVAL; + + buffer = memdup_user_nul(buf, len); + if (IS_ERR(buffer)) + return PTR_ERR(buffer); + + __log_info("buffer = %s [%d]\n", buffer, (int)len); + err = lua_module_register(buffer, len); + kfree(buffer); + if (err < 0) + return err; + + return len; +} + +static const struct file_operations fops_register = { + .write = register_write, +}; + + +static ssize_t unregister_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + char *buffer, *p; + int err; + + if (!lua_capable(CAP_MAC_ADMIN)) + return -EPERM; + + /* No partial writes. */ + if (*ppos != 0) + return -EINVAL; + if (len == 0) + return -EINVAL; + + buffer = memdup_user_nul(buf, len); + if (IS_ERR(buffer)) + return PTR_ERR(buffer); + + /* remove the tailing '\n' */ + p = &buffer[len - 1]; + while (p >= buffer && *p == '\n') + *p-- = '\0'; + __log_info("buffer = %s [%d]\n", buffer, (int)len); + err = lua_module_unregister(buffer); + kfree(buffer); + if (err < 0) + return err; + + return len; +} + +static const struct file_operations fops_unregister = { + .write = unregister_write, +}; + + +static int open_module(struct inode *inode, struct file *filp) +{ + return single_open(filp, modules_show, NULL); +} + +static const struct file_operations fops_module = { + .open = open_module, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +#ifdef CONFIG_SECURITY_LUA_LSM_STATS + +static int open_status(struct inode *inode, struct file *filp) +{ + return single_open(filp, lua_lsm_status_show, NULL); +} + +static const struct file_operations fops_status = { + .open = open_status, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int open_lsm_funcs(struct inode *inode, struct file *filp) +{ + return single_open(filp, lsmhook_stat_show, NULL); +} + +static const struct file_operations fops_lsm_funcs = { + .open = open_lsm_funcs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +#endif + +static struct lua_file { + const char *name; + umode_t mode; + const struct file_operations *fops; + struct dentry *dentry; +} files[] = { + { "version", 0444, &fops_version }, /* r--r--r-- */ + { "register", 0222, &fops_register }, /* -w--w--w- */ + { "unregister", 0222, &fops_unregister }, /* -w--w--w- */ + { "module", 0444, &fops_module }, /* r--r--r-- */ +#ifdef CONFIG_SECURITY_LUA_LSM_STATS + { "status", 0444, &fops_status }, /* r--r--r-- */ + { "lsm_funcs", 0444, &fops_lsm_funcs }, /* r--r--r-- */ +#endif + { NULL, 0, NULL } +}; + +static int __init lua_securityfs_init(void) +{ + struct dentry *dir; + struct dentry *dentry; + struct lua_file *file; + + dir = securityfs_create_dir("lua", NULL); + if (IS_ERR(dir)) + return PTR_ERR(dir); + + for (file = files; file->name; file++) { + dentry = securityfs_create_file(file->name, file->mode, + dir, NULL, file->fops); + if (IS_ERR(dentry)) + break; + file->dentry = dentry; + } + + if (IS_ERR(dentry)) { + for (file--; file >= files; file--) + securityfs_remove(file->dentry); + + securityfs_remove(dir); + return PTR_ERR(dentry); + } + + return 0; +} +fs_initcall(lua_securityfs_init); From b7f983fa07345d0224083144f7d57466603ca0d0 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Wed, 27 Aug 2025 18:58:46 +0800 Subject: [PATCH 024/110] lua-lsm: Fix deadlock on modules_lock The reason is that the modules_lock is held for a period of time during module registration. A softirq occurs within this critical section, and the softirq handler reenters the LSM function, causing a deadlock. This issue can be resolved by disabling softirq while holding the modules_lock lock during module registration. Some logs are as follows: [28031.294632] rcu: INFO: rcu_preempt self-detected stall on CPU [28031.294801] rcu: 2-...!: (886811 ticks this GP) idle=ea9c/1/0x4000000000000000 softirq=243287/243287 fqs=0 [28031.295043] rcu: (t=887418 jiffies g=263373 q=454 ncpus=4) [28031.295183] rcu: rcu_preempt kthread starved for 887418 jiffies! g263373 f0x0 RCU_GP_WAIT_FQS(5) ->state=0x0 ->cpu=2 [28031.295524] rcu: Unless rcu_preempt kthread gets sufficient CPU time, OOM is now expected behavior. [28031.295872] rcu: RCU grace-period kthread stack dump: [28031.296001] task:rcu_preempt state:R running task stack:0 pid:15 tgid:15 ppid:2 task_flags:0x208040 flags:0x00000010 [28031.296319] Call trace: [28031.296381] __switch_to+0x194/0x2b4 (T) [28031.296484] __schedule+0x5b4/0x980 [28031.296572] schedule+0x54/0xf8 [28031.296652] schedule_timeout+0x88/0xf8 [28031.296750] rcu_gp_fqs_loop+0x1c4/0x6b0 [28031.296851] rcu_gp_kthread+0x60/0x134 [28031.296946] kthread+0x140/0x254 [28031.297030] ret_from_fork+0x10/0x20 [28031.297123] Sending NMI from CPU 2 to CPUs 1: [28031.297242] NMI backtrace for cpu 1 [28031.297247] CPU: 1 UID: 0 PID: 81146 Comm: sh Not tainted 6.17.0-rc3+ #511 PREEMPT [28031.297249] Hardware name: QEMU QEMU Virtual Machine, BIOS edk2-stable202408-prebuilt.qemu.org 08/13/2024 [28031.297250] pstate: 01400005 (nzcv daif +PAN -UAO -TCO +DIT -SSBS BTYPE=--) [28031.297250] pc : queued_read_lock_slowpath+0x80/0x14c [28031.297253] lr : _raw_read_lock_bh+0x64/0x68 [28031.297254] sp : ffff800088ee39e0 [28031.297254] x29: ffff800088ee39e0 x28: 0000000000002e2e x27: 0000000000000501 [28031.297256] x26: 0000000000000030 x25: 2f2f2f2f2f2f2f2f x24: 61c8864680b583eb [28031.297257] x23: fefefefefefefeff x22: 0000000000000000 x21: ffff0000c2671138 [28031.297257] x20: 0000000000000081 x19: 00001643fef959e4 x18: 0000000000000000 [28031.297258] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000 [28031.297259] x14: 0000000000000000 x13: 0000000000000000 x12: 8080808080808000 [28031.297259] x11: 000000000000002e x10: 0000000000000018 x9 : 0000000000000000 [28031.297260] x8 : 00000000000008ff x7 : 0000000000000000 x6 : 0000000000000000 [28031.297261] x5 : 0000000000000000 x4 : 0000000000000000 x3 : ffff800088ee3c98 [28031.297261] x2 : 0000000000000000 x1 : 0000000000013cee x0 : ffff8000818ae3c8 [28031.297262] Call trace: [28031.297262] queued_read_lock_slowpath+0x80/0x14c (P) [28031.297264] _raw_read_lock_bh+0x64/0x68 [28031.297265] lua_lsm_inode_permission+0x54/0x35c [28031.297268] security_inode_permission+0x54/0xb0 [28031.297269] inode_permission+0x64/0x170 [28031.297270] link_path_walk+0xb4/0x388 [28031.297272] path_lookupat+0x68/0x120 [28031.297272] filename_lookup+0xe0/0x1e0 [28031.297273] vfs_statx+0x7c/0x1a0 [28031.297276] vfs_fstatat+0xb4/0xe0 [28031.297277] __arm64_sys_newfstatat+0x68/0xa8 [28031.297278] invoke_syscall+0x40/0xf8 [28031.297280] el0_svc_common+0xa8/0xd8 [28031.297281] do_el0_svc+0x1c/0x28 [28031.297282] el0_svc+0x38/0x8c [28031.297283] el0t_64_sync_handler+0x84/0x12c [28031.297284] el0t_64_sync+0x198/0x19c [28031.298239] CPU: 2 UID: 0 PID: 81145 Comm: cat Not tainted 6.17.0-rc3+ #511 PREEMPT [28031.303082] Hardware name: QEMU QEMU Virtual Machine, BIOS edk2-stable202408-prebuilt.qemu.org 08/13/2024 [28031.303326] pstate: 01400005 (nzcv daif +PAN -UAO -TCO +DIT -SSBS BTYPE=--) [28031.303504] pc : queued_read_lock_slowpath+0x80/0x14c [28031.303634] lr : _raw_read_lock_bh+0x64/0x68 [28031.303743] sp : ffff8000818ebe00 [28031.303828] x29: ffff8000818ebe00 x28: 0000000000000200 x27: 000000000000000a [28031.304011] x26: ffff800081788000 x25: ffff8000800fa104 x24: 0000000000000001 [28031.304192] x23: 0000000000000000 x22: 000000000000000a x21: 0000000000000000 [28031.304374] x20: ffff000008774600 x19: 00001643fef8ae04 x18: 0000000000000000 [28031.304556] x17: ffff80007e099000 x16: ffff8000818e8000 x15: 0000000000000000 [28031.304739] x14: 0000000000000004 x13: ffff0000ff6187a8 x12: 0000000000000002 [28031.304922] x11: ffff0000c2288a18 x10: 0000000000000018 x9 : 0000000000000000 [28031.305104] x8 : 00000000000008ff x7 : 7fffffffffffffff x6 : 00003d0900007d00 [28031.305285] x5 : ffff800081874120 x4 : 0000000000000008 x3 : ffff80008a153710 [28031.305470] x2 : 00000000000404d4 x1 : 00000000000138ce x0 : ffff8000818ae3c8 [28031.305651] Call trace: [28031.305714] queued_read_lock_slowpath+0x80/0x14c (P) [28031.305844] _raw_read_lock_bh+0x64/0x68 [28031.305945] lua_lsm_cred_free+0x54/0x2bc [28031.306048] security_cred_free+0x5c/0x90 [28031.306151] put_cred_rcu+0x28/0x15c [28031.306244] rcu_core+0x2c0/0x5e0 [28031.306330] rcu_core_si+0x10/0x1c [28031.306418] handle_softirqs+0xdc/0x200 [28031.306517] __do_softirq+0x14/0x20 [28031.306686] ____do_softirq+0x10/0x1c [28031.306782] call_on_irq_stack+0x30/0x48 [28031.306884] do_softirq_own_stack+0x1c/0x28 [28031.306993] __irq_exit_rcu+0x54/0xf8 [28031.307087] irq_exit_rcu+0x10/0x1c [28031.307176] el1_interrupt+0x38/0x54 [28031.307269] el1h_64_irq_handler+0x18/0x24 [28031.307375] el1h_64_irq+0x6c/0x70 [28031.307463] lua_module_register+0x550/0x588 (P) [28031.307582] register_write+0x78/0xac [28031.307676] vfs_write+0x160/0x3bc [28031.307764] ksys_write+0x70/0xe4 [28031.307849] __arm64_sys_write+0x1c/0x28 [28031.307950] invoke_syscall+0x40/0xf8 [28031.308044] el0_svc_common+0xa8/0xd8 [28031.308138] do_el0_svc+0x1c/0x28 [28031.308225] el0_svc+0x38/0x8c [28031.308311] el0t_64_sync_handler+0x84/0x12c [28031.308422] el0t_64_sync+0x198/0x19c --- security/lua/lsm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/security/lua/lsm.c b/security/lua/lsm.c index d3e2e9f755e4..6110de93ecc7 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -526,7 +526,7 @@ int lua_module_register(const char *code, size_t len) TAILQ_INIT(&module->kvnodes); spin_lock_init(&module->kvnodes_lock); - write_lock(&modules_lock); + write_lock_bh(&modules_lock); TAILQ_FOREACH(m, &lsm_modules, list) { if (strcmp(module->name, m->name) == 0) break; @@ -539,7 +539,7 @@ int lua_module_register(const char *code, size_t len) atomic_inc(&lua_lsm_hook_stats[i].nhooks); } } - write_unlock(&modules_lock); + write_unlock_bh(&modules_lock); err = -EEXIST; if (m != NULL) { @@ -661,13 +661,13 @@ int modules_show(struct seq_file *m, void *v) "name", "license", "size", "nhooks", "author"); seq_printf(m, "%s\n", TABLINE); - read_lock(&modules_lock); + read_lock_bh(&modules_lock); TAILQ_FOREACH(module, &lsm_modules, list) { seq_printf(m, "%-12s %-10s %6zu %6d %-48s\n", module->name, module->license, module->chunk_len, module->nhooks, module->author); } - read_unlock(&modules_lock); + read_unlock_bh(&modules_lock); return 0; } From 5252b52a0b93983f8a05dc7c9df0071257c0aa1e Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Fri, 29 Aug 2025 11:25:09 +0800 Subject: [PATCH 025/110] lua-lsm: Fix potential deadlock When interrupts are disabled, file system operations triggered by memory allocation may cause a potential deadlock. The log is as follows: [ 37.041540] ===================================================== [ 37.041790] WARNING: SOFTIRQ-safe -> SOFTIRQ-unsafe lock order detected [ 37.042170] 6.17.0-rc3+ #515 Tainted: G W [ 37.042492] ----------------------------------------------------- [ 37.042953] cat/1610 [HC0[0]:SC0[2]:HE1:SE0] is trying to acquire: [ 37.043316] ffff800081f2b9f8 (fs_reclaim){+.+.}-{0:0}, at: __kmalloc_noprof+0xb0/0x4d4 [ 37.043783] [ 37.043783] and this task is already holding: [ 37.044121] ffff0000c177e138 (&dict->lock){+.-.}-{3:3}, at: kvcache_incr+0x68/0x21c [ 37.044569] which would create a new lock dependency: [ 37.044861] (&dict->lock){+.-.}-{3:3} -> (fs_reclaim){+.+.}-{0:0} [ 37.045222] [ 37.045222] but this new dependency connects a SOFTIRQ-irq-safe lock: [ 37.045685] (&dict->lock){+.-.}-{3:3} [ 37.045687] [ 37.045687] ... which became SOFTIRQ-irq-safe at: [ 37.046273] lock_acquire+0x118/0x26c [ 37.046479] _raw_write_lock+0x4c/0x88 [ 37.046736] kvcache_dict_free+0x24/0x104 [ 37.046950] lua_lsm_inode_free_security_rcu+0x1d4/0x274 [ 37.047231] inode_free_by_rcu+0x54/0x9c [ 37.047442] rcu_core+0x474/0xa50 [ 37.047623] rcu_core_si+0x10/0x1c [ 37.047805] handle_softirqs+0x178/0x42c [ 37.048018] __do_softirq+0x14/0x20 [ 37.048205] ____do_softirq+0x10/0x1c [ 37.048404] call_on_irq_stack+0x30/0x48 [ 37.048614] do_softirq_own_stack+0x1c/0x28 [ 37.048838] __irq_exit_rcu+0xd4/0x194 [ 37.049039] irq_exit_rcu+0x10/0x34 [ 37.049226] el1_interrupt+0x38/0x54 [ 37.049418] el1h_64_irq_handler+0x18/0x24 [ 37.049636] el1h_64_irq+0x6c/0x70 [ 37.049818] do_idle+0xe8/0x26c [ 37.049994] cpu_startup_entry+0x34/0x38 [ 37.050206] kernel_init+0x0/0x128 [ 37.050389] start_kernel+0x304/0x3c0 [ 37.050659] __primary_switched+0x88/0x90 [ 37.050881] [ 37.050881] to a SOFTIRQ-irq-unsafe lock: [ 37.051169] (fs_reclaim){+.+.}-{0:0} [ 37.051171] [ 37.051171] ... which became SOFTIRQ-irq-unsafe at: [ 37.051699] ... [ 37.051704] lock_acquire+0x118/0x26c [ 37.051997] fs_reclaim_acquire+0x64/0xd0 [ 37.052215] mem_cgroup_css_alloc+0xdc/0x6e4 [ 37.052436] cgroup_init_subsys+0x7c/0x1d0 [ 37.052642] cgroup_init+0x2d8/0x490 [ 37.052822] start_kernel+0x2f4/0x3c0 [ 37.053006] __primary_switched+0x88/0x90 [ 37.053210] [ 37.053210] other info that might help us debug this: [ 37.053210] [ 37.053598] Possible interrupt unsafe locking scenario: [ 37.053598] [ 37.053929] CPU0 CPU1 [ 37.054152] ---- ---- [ 37.054372] lock(fs_reclaim); [ 37.054649] local_irq_disable(); [ 37.055469] lock(&dict->lock); [ 37.055751] lock(fs_reclaim); [ 37.056024] [ 37.056151] lock(&dict->lock); [ 37.056316] [ 37.056316] *** DEADLOCK *** [ 37.056316] [ 37.056602] 2 locks held by cat/1610: [ 37.056779] #0: ffff800081fdaf18 (modules_lock){++.-}-{3:3}, at: lua_lsm_file_permission+0x58/0x36c [ 37.057221] #1: ffff0000c177e138 (&dict->lock){+.-.}-{3:3}, at: kvcache_incr+0x68/0x21c [ 37.057611] [ 37.057611] the dependencies between SOFTIRQ-irq-safe lock and the holding lock: [ 37.058091] -> (&dict->lock){+.-.}-{3:3} { [ 37.058303] HARDIRQ-ON-W at: [ 37.058495] lock_acquire+0x118/0x26c [ 37.058777] _raw_write_lock+0x4c/0x88 [ 37.059053] kvcache_dict_free+0x24/0x104 [ 37.059353] lua_lsm_file_free_security+0x210/0x2bc [ 37.059691] security_file_free+0x5c/0xa4 [ 37.059991] __fput+0x1a0/0x2f0 [ 37.060242] delayed_fput+0x44/0x58 [ 37.060514] process_one_work+0x210/0x548 [ 37.060815] worker_thread+0x244/0x380 [ 37.061096] kthread+0x138/0x260 [ 37.061348] ret_from_fork+0x10/0x20 [ 37.061622] IN-SOFTIRQ-W at: [ 37.061790] lock_acquire+0x118/0x26c [ 37.062117] _raw_write_lock+0x4c/0x88 [ 37.062405] kvcache_dict_free+0x24/0x104 [ 37.062730] lua_lsm_inode_free_security_rcu+0x1d4/0x274 [ 37.063097] inode_free_by_rcu+0x54/0x9c [ 37.063390] rcu_core+0x474/0xa50 [ 37.063652] rcu_core_si+0x10/0x1c [ 37.063917] handle_softirqs+0x178/0x42c [ 37.064213] __do_softirq+0x14/0x20 [ 37.064475] ____do_softirq+0x10/0x1c [ 37.064739] call_on_irq_stack+0x30/0x48 [ 37.065014] do_softirq_own_stack+0x1c/0x28 [ 37.065307] __irq_exit_rcu+0xd4/0x194 [ 37.065583] irq_exit_rcu+0x10/0x34 [ 37.065901] el1_interrupt+0x38/0x54 [ 37.066155] el1h_64_irq_handler+0x18/0x24 [ 37.066399] el1h_64_irq+0x6c/0x70 [ 37.066660] do_idle+0xe8/0x26c [ 37.066891] cpu_startup_entry+0x34/0x38 [ 37.067168] kernel_init+0x0/0x128 [ 37.067418] start_kernel+0x304/0x3c0 [ 37.067679] __primary_switched+0x88/0x90 [ 37.067959] INITIAL USE at: [ 37.068115] lock_acquire+0x118/0x26c [ 37.068375] _raw_write_lock+0x4c/0x88 [ 37.068637] kvcache_dict_free+0x24/0x104 [ 37.068917] lua_lsm_file_free_security+0x210/0x2bc [ 37.069222] security_file_free+0x5c/0xa4 [ 37.069499] __fput+0x1a0/0x2f0 [ 37.069726] delayed_fput+0x44/0x58 [ 37.070043] process_one_work+0x210/0x548 [ 37.070282] worker_thread+0x244/0x380 [ 37.070499] kthread+0x138/0x260 [ 37.070713] ret_from_fork+0x10/0x20 [ 37.070923] } [ 37.070992] ... key at: [] kvcache_dict_init.__key+0x0/0x10 [ 37.071308] [ 37.071308] the dependencies between the lock to be acquired [ 37.071309] and SOFTIRQ-irq-unsafe lock: [ 37.071759] -> (fs_reclaim){+.+.}-{0:0} { [ 37.071923] HARDIRQ-ON-W at: [ 37.072052] lock_acquire+0x118/0x26c [ 37.072268] fs_reclaim_acquire+0x64/0xd0 [ 37.072499] mem_cgroup_css_alloc+0xdc/0x6e4 [ 37.072740] cgroup_init_subsys+0x7c/0x1d0 [ 37.072974] cgroup_init+0x2d8/0x490 [ 37.073187] start_kernel+0x2f4/0x3c0 [ 37.073403] __primary_switched+0x88/0x90 [ 37.073633] SOFTIRQ-ON-W at: [ 37.073761] lock_acquire+0x118/0x26c [ 37.074010] fs_reclaim_acquire+0x64/0xd0 [ 37.074241] mem_cgroup_css_alloc+0xdc/0x6e4 [ 37.074482] cgroup_init_subsys+0x7c/0x1d0 [ 37.074735] cgroup_init+0x2d8/0x490 [ 37.074947] start_kernel+0x2f4/0x3c0 [ 37.075160] __primary_switched+0x88/0x90 [ 37.075388] INITIAL USE at: [ 37.075512] lock_acquire+0x118/0x26c [ 37.075722] fs_reclaim_acquire+0x64/0xd0 [ 37.075944] mem_cgroup_css_alloc+0xdc/0x6e4 [ 37.076169] cgroup_init_subsys+0x7c/0x1d0 [ 37.076399] cgroup_init+0x2d8/0x490 [ 37.076598] start_kernel+0x2f4/0x3c0 [ 37.076798] __primary_switched+0x88/0x90 [ 37.077012] } [ 37.077078] ... key at: [] __fs_reclaim_map+0x0/0x30 [ 37.077349] ... acquired at: [ 37.077464] fs_reclaim_acquire+0x64/0xd0 [ 37.077628] __kmalloc_noprof+0xb0/0x4d4 [ 37.077787] kvcache_incr+0xe0/0x21c [ 37.077967] shdict_incr+0x34/0x44 [ 37.078108] luaD_precall+0x330/0x674 [ 37.078259] luaV_execute+0xaac/0x119c [ 37.078413] luaD_call+0xb0/0x128 [ 37.078550] f_call+0x1c/0x28 [ 37.078692] luaD_rawrunprotected+0x7c/0xb8 [ 37.078862] luaD_pcall+0x40/0x184 [ 37.079003] lua_pcall+0xac/0x190 [ 37.079191] lua_lsm_file_permission+0x1fc/0x36c [ 37.079379] security_file_permission+0x4c/0xa8 [ 37.079562] rw_verify_area+0x54/0x138 [ 37.079715] vfs_read+0xa4/0x2b0 [ 37.079846] ksys_read+0x70/0xe4 [ 37.079974] __arm64_sys_read+0x1c/0x28 [ 37.080123] invoke_syscall+0x40/0xf8 [ 37.080269] el0_svc_common+0xa8/0xd8 [ 37.080413] do_el0_svc+0x1c/0x28 [ 37.080544] el0_svc+0x50/0xcc [ 37.080665] el0t_64_sync_handler+0x84/0x12c [ 37.080831] el0t_64_sync+0x198/0x19c [ 37.080975] [ 37.081031] [ 37.081031] stack backtrace: [ 37.081195] CPU: 1 UID: 0 PID: 1610 Comm: cat Tainted: G W 6.17.0-rc3+ #515 PREEMPT [ 37.081535] Tainted: [W]=WARN [ 37.081646] Hardware name: QEMU QEMU Virtual Machine, BIOS edk2-stable202408-prebuilt.qemu.org 08/13/2024 [ 37.082039] Call trace: [ 37.082130] show_stack+0x18/0x24 (C) [ 37.082269] __dump_stack+0x28/0x38 [ 37.082400] dump_stack_lvl+0x64/0x84 [ 37.082537] dump_stack+0x18/0x24 [ 37.082688] __lock_acquire+0x2b2c/0x2b90 [ 37.082837] lock_acquire+0x118/0x26c [ 37.082972] fs_reclaim_acquire+0x64/0xd0 [ 37.083119] __kmalloc_noprof+0xb0/0x4d4 [ 37.083264] kvcache_incr+0xe0/0x21c [ 37.083395] shdict_incr+0x34/0x44 [ 37.083520] luaD_precall+0x330/0x674 [ 37.083656] luaV_execute+0xaac/0x119c [ 37.083793] luaD_call+0xb0/0x128 [ 37.083916] f_call+0x1c/0x28 [ 37.084025] luaD_rawrunprotected+0x7c/0xb8 [ 37.084180] luaD_pcall+0x40/0x184 [ 37.084306] lua_pcall+0xac/0x190 [ 37.084428] lua_lsm_file_permission+0x1fc/0x36c [ 37.084598] security_file_permission+0x4c/0xa8 [ 37.084765] rw_verify_area+0x54/0x138 [ 37.084902] vfs_read+0xa4/0x2b0 [ 37.085021] ksys_read+0x70/0xe4 [ 37.085139] __arm64_sys_read+0x1c/0x28 [ 37.085280] invoke_syscall+0x40/0xf8 [ 37.085416] el0_svc_common+0xa8/0xd8 [ 37.085553] do_el0_svc+0x1c/0x28 [ 37.085676] el0_svc+0x50/0xcc [ 37.085790] el0t_64_sync_handler+0x84/0x12c [ 37.086018] el0t_64_sync+0x198/0x19c --- security/lua/kvcache.c | 4 ++-- security/lua/lsm.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/security/lua/kvcache.c b/security/lua/kvcache.c index 965590a76f9b..fd6d290830b3 100644 --- a/security/lua/kvcache.c +++ b/security/lua/kvcache.c @@ -65,7 +65,7 @@ kvcache_node_alloc(struct kvcache_dict *dict, struct lua_module *module, struct kvcache_node *node; l += key ? (len + 1) : 0; - node = kmalloc(l, GFP_KERNEL); + node = kmalloc(l, GFP_NOFS); if (node == NULL) return NULL; @@ -199,7 +199,7 @@ static int kvcache_node_fill(lua_State *L, int idx, struct kvcache_node *node) case LUA_TSTRING: s = lua_tolstring(L, idx, &node->s.l); - node->s.s = kmalloc(node->s.l + 1, GFP_KERNEL); + node->s.s = kmalloc(node->s.l + 1, GFP_NOFS); if (node->s.s == NULL) return -ENOMEM; memcpy((void *)node->s.s, s, node->s.l + 1/* ending 0 */); diff --git a/security/lua/lsm.c b/security/lua/lsm.c index 6110de93ecc7..87457863a250 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -285,7 +285,7 @@ static void *lvm_alloc(void *ud, void *ptr, size_t osize, size_t nsize) atomic_inc(&mem_nrealloc); else atomic_inc(&mem_nalloc); - return krealloc(ptr, nsize, GFP_KERNEL); + return krealloc(ptr, nsize, GFP_NOFS); } } From 5e4644f542c04c4e993172f221b18253e3ba7c46 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Wed, 3 Sep 2025 20:25:18 +0800 Subject: [PATCH 026/110] lua-lsm: optimizate dict free --- security/lua/kvcache.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/security/lua/kvcache.c b/security/lua/kvcache.c index fd6d290830b3..aa1253014417 100644 --- a/security/lua/kvcache.c +++ b/security/lua/kvcache.c @@ -113,12 +113,6 @@ static void kvcache_node_clear(struct kvcache_node *node) static void kvcache_node_free(struct kvcache_node *node) { - if (node->module) { - unsigned long flags; - spin_lock_irqsave(&node->module->kvnodes_lock, flags); - TAILQ_REMOVE(&node->module->kvnodes, node, modlist); - spin_unlock_irqrestore(&node->module->kvnodes_lock, flags); - } kvcache_node_clear(node); atomic_inc(&node_nfree); kfree(node); @@ -555,7 +549,6 @@ void kvcache_module_nodes_gc(struct lua_module *module) write_unlock(&dict->lock); atomic_dec(&dict->count); - TAILQ_REMOVE(&cleanup_list, node, modlist); kvcache_node_free(node); } } @@ -566,9 +559,8 @@ void kvcache_dict_free(struct kvcache_dict *dict) write_lock(&dict->lock); RB_FOREACH_SAFE(node, kvcache, &dict->root, n) { - RB_REMOVE(kvcache, &dict->root, node); + kvcache_module_unlink(dict, node->module, node); kvcache_node_free(node); - atomic_dec(&dict->count); } WARN_ON(atomic_read(&dict->count) != 0); write_unlock(&dict->lock); From d9a4446cbc39325529c73d4fa0513692536d3085 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Fri, 5 Sep 2025 10:30:04 +0800 Subject: [PATCH 027/110] lua-lsm: dont swap the module and Lua VM memeory --- security/lua/lsm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/security/lua/lsm.c b/security/lua/lsm.c index 87457863a250..6d81e1e9b146 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -285,7 +285,7 @@ static void *lvm_alloc(void *ud, void *ptr, size_t osize, size_t nsize) atomic_inc(&mem_nrealloc); else atomic_inc(&mem_nalloc); - return krealloc(ptr, nsize, GFP_NOFS); + return krealloc(ptr, nsize, GFP_ATOMIC); } } @@ -432,7 +432,7 @@ int lua_module_register(const char *code, size_t len) __log_info("lua module loaded, top = %d\n", lua_gettop(L)); err = -ENOMEM; - module = kzalloc(sizeof(*module), GFP_KERNEL); + module = kzalloc(sizeof(*module), GFP_ATOMIC); if (module == NULL) goto err_free_lua; @@ -513,7 +513,7 @@ int lua_module_register(const char *code, size_t len) if (module->name == NULL) goto err_free_module; - module->chunk = kmalloc(chunk_len, GFP_KERNEL); + module->chunk = kmalloc(chunk_len, GFP_ATOMIC); if (module->chunk == NULL) goto err_free_module; memcpy(module->chunk, chunk, chunk_len); From c3db729e49bf3ee04448d2a329eb5008aa0960b6 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Fri, 5 Sep 2025 11:11:05 +0800 Subject: [PATCH 028/110] lua-lsm: add DEBUG config --- security/lua/Kconfig | 13 +++++++++++-- security/lua/Makefile | 2 ++ security/lua/lsm.c | 4 ++-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/security/lua/Kconfig b/security/lua/Kconfig index 355666748758..87192cad3a07 100644 --- a/security/lua/Kconfig +++ b/security/lua/Kconfig @@ -9,10 +9,19 @@ config SECURITY_LUA_LSM This selects the Lua based LSM. If you are unsure how to answer this question, answer N. -config SECURITY_LUA_LSM_STATS - bool "lua-lsm statistics" +config SECURITY_LUA_LSM_STATISTICS + bool "Lua-LSM Statistics" depends on SECURITY_LUA_LSM default n help This selects the lua-lsm statistics. If you are unsure how to answer this question, answer N. + +config SECURITY_LUA_LSM_DEBUG + bool "Lua-LSM kernel debugging support" + depends on SECURITY_LUA_LSM + default n + help + This enables debugging code designed to help Lua-LSM kernel + developers, unless you know what this does in the kernel code you + should leave this disabled. diff --git a/security/lua/Makefile b/security/lua/Makefile index eec55de1ac0e..f5b4f62da812 100644 --- a/security/lua/Makefile +++ b/security/lua/Makefile @@ -5,6 +5,8 @@ obj-$(CONFIG_SECURITY_LUA_LSM) := lua-lsm.o +ccflags-$(CONFIG_SECURITY_LUA_LSM_DEBUG) += -DDEBUG + lua-lsm-y := lsm.o securityfs.o auxlib.o kvcache.o lsm_defs.o \ lua_kernel.o lua_fs.o lua_net.o \ lua_errno.o lua_capability.o lua_signal.o diff --git a/security/lua/lsm.c b/security/lua/lsm.c index 6d81e1e9b146..fd81c613e0d7 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -547,7 +547,7 @@ int lua_module_register(const char *code, size_t len) goto err_free_module; } - __log_info("module <%s> registered with %d filters\n", + pr_info("module <%s> registered with %d filters\n", module->name, module->nhooks); lua_state_free(L); @@ -638,7 +638,7 @@ int lua_module_unregister(const char *name) write_unlock_bh(&modules_lock); - __log_info("unregistered module <%s> from %d/%d Lua VMs.\n", + pr_info("unregistered module <%s> from %d/%d Lua VMs.\n", name, count, atomic_read(&vm_nalloc) - atomic_read(&vm_nfree)); kfree(module->chunk); From 2840f9ba6f9daabef899ee42349d72ca2ab76801 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Tue, 9 Sep 2025 10:02:46 +0800 Subject: [PATCH 029/110] lua-lsm: kvcache - Dont disable irq on lock --- security/lua/kvcache.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/security/lua/kvcache.c b/security/lua/kvcache.c index aa1253014417..0b9650d69181 100644 --- a/security/lua/kvcache.c +++ b/security/lua/kvcache.c @@ -125,10 +125,9 @@ kvcache_module_link(struct kvcache_dict *dict, struct lua_module *module, RB_INSERT(kvcache, &dict->root, node); atomic_inc(&dict->count); if (module) { - unsigned long flags; - spin_lock_irqsave(&module->kvnodes_lock, flags); + spin_lock(&module->kvnodes_lock); TAILQ_INSERT_TAIL(&module->kvnodes, node, modlist); - spin_unlock_irqrestore(&module->kvnodes_lock, flags); + spin_unlock(&module->kvnodes_lock); } } @@ -139,10 +138,9 @@ kvcache_module_unlink(struct kvcache_dict *dict, struct lua_module *module, RB_REMOVE(kvcache, &dict->root, node); atomic_dec(&dict->count); if (module) { - unsigned long flags; - spin_lock_irqsave(&module->kvnodes_lock, flags); + spin_lock(&module->kvnodes_lock); TAILQ_REMOVE(&module->kvnodes, node, modlist); - spin_unlock_irqrestore(&module->kvnodes_lock, flags); + spin_unlock(&module->kvnodes_lock); } } @@ -533,12 +531,11 @@ void kvcache_module_nodes_gc(struct lua_module *module) { TAILQ_HEAD(, kvcache_node) cleanup_list; struct kvcache_node *node, *tmp; - unsigned long flags; TAILQ_INIT(&cleanup_list); - spin_lock_irqsave(&module->kvnodes_lock, flags); + spin_lock(&module->kvnodes_lock); TAILQ_SWAP(&module->kvnodes, &cleanup_list, kvcache_node, modlist); - spin_unlock_irqrestore(&module->kvnodes_lock, flags); + spin_unlock(&module->kvnodes_lock); TAILQ_FOREACH_SAFE(node, &cleanup_list, modlist, tmp) { struct kvcache_dict *dict = node->dict; From 3da7919be8a9e6770083c9ff693dc335e07c92ce Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Tue, 9 Sep 2025 10:04:11 +0800 Subject: [PATCH 030/110] lua-lsm: kvcache - kmalloc in atomic context --- security/lua/kvcache.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/security/lua/kvcache.c b/security/lua/kvcache.c index 0b9650d69181..ba5cf2fb8ff2 100644 --- a/security/lua/kvcache.c +++ b/security/lua/kvcache.c @@ -65,7 +65,7 @@ kvcache_node_alloc(struct kvcache_dict *dict, struct lua_module *module, struct kvcache_node *node; l += key ? (len + 1) : 0; - node = kmalloc(l, GFP_NOFS); + node = kmalloc(l, GFP_ATOMIC); if (node == NULL) return NULL; @@ -191,7 +191,7 @@ static int kvcache_node_fill(lua_State *L, int idx, struct kvcache_node *node) case LUA_TSTRING: s = lua_tolstring(L, idx, &node->s.l); - node->s.s = kmalloc(node->s.l + 1, GFP_NOFS); + node->s.s = kmalloc(node->s.l + 1, GFP_ATOMIC); if (node->s.s == NULL) return -ENOMEM; memcpy((void *)node->s.s, s, node->s.l + 1/* ending 0 */); From b79078e88d467e1cc0ad0bdee1523a4a54a60e2a Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Tue, 9 Sep 2025 10:05:14 +0800 Subject: [PATCH 031/110] lua-lsm: display more information on unregister --- security/lua/lsm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/security/lua/lsm.c b/security/lua/lsm.c index fd81c613e0d7..6d753d2dedd8 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -604,6 +604,8 @@ int lua_module_unregister(const char *name) return -ENOENT; } + pr_info("Prepare to unregister module <%s> ...\n", name); + for (i = 0; lua_lsm_hook_stats[i].name; i++) { if (__BITMAP_ISSET(i, &module->hookfuncs)) atomic_dec(&lua_lsm_hook_stats[i].nhooks); @@ -638,7 +640,7 @@ int lua_module_unregister(const char *name) write_unlock_bh(&modules_lock); - pr_info("unregistered module <%s> from %d/%d Lua VMs.\n", + pr_info("Unregistered module <%s> from %d/%d Lua VMs.\n", name, count, atomic_read(&vm_nalloc) - atomic_read(&vm_nfree)); kfree(module->chunk); From c10e915d398fb1852f5e17fad314b6be2fe5f4ef Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Thu, 11 Sep 2025 14:18:33 +0800 Subject: [PATCH 032/110] lua-lsm: Donot initialize securityfs if LSM is not initialized Do not initialize securityfs if LSM is not initialized to avoid accessing uninitialized data and triggering a crash. Signed-off-by: Tianjia Zhang --- security/lua/lsm.c | 7 +++++++ security/lua/lsm.h | 2 ++ security/lua/securityfs.c | 3 +++ 3 files changed, 12 insertions(+) diff --git a/security/lua/lsm.c b/security/lua/lsm.c index 6d753d2dedd8..ed83e440b349 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -41,6 +41,9 @@ __setup("lua.nodebug", disable_debug); #endif /* ! DEBUG */ +/* Flag indicating whether initialization completed */ +int lua_lsm_initialized __initdata; + /********************************* lsm hook *********************************/ struct lua_modules_head lsm_modules = TAILQ_HEAD_INITIALIZER(lsm_modules); @@ -825,6 +828,10 @@ static int __init lua_lsm_init(void) return err; security_add_hooks(lua_lsm_hooks, ARRAY_SIZE(lua_lsm_hooks), &lua_lsmid); + + /* Report that Lua-LSM successfully initialized */ + lua_lsm_initialized = 1; + pr_info("Lua based LSM initialized\n"); return 0; } diff --git a/security/lua/lsm.h b/security/lua/lsm.h index d66937406ef2..10219cba8806 100644 --- a/security/lua/lsm.h +++ b/security/lua/lsm.h @@ -20,6 +20,8 @@ #include "queue.h" #include "kvcache.h" +/* Flag indicating whether initialization completed */ +extern int lua_lsm_initialized __initdata; #define LUA_LSM_VERSION 1 diff --git a/security/lua/securityfs.c b/security/lua/securityfs.c index 1b83517b95ef..22fef3553cc6 100644 --- a/security/lua/securityfs.c +++ b/security/lua/securityfs.c @@ -185,6 +185,9 @@ static int __init lua_securityfs_init(void) struct dentry *dentry; struct lua_file *file; + if (!lua_lsm_initialized) + return 0; + dir = securityfs_create_dir("lua", NULL); if (IS_ERR(dir)) return PTR_ERR(dir); From 5c8aec4e54059e402aa1a1efae43df756386fbbb Mon Sep 17 00:00:00 2001 From: Jianye Gao Date: Wed, 17 Sep 2025 20:29:20 +0800 Subject: [PATCH 033/110] lua-lsm: fix: prevent returning true for non-existent key --- security/lua/lua_fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/lua/lua_fs.c b/security/lua/lua_fs.c index ebc0224719ad..6ec4a9f8017b 100644 --- a/security/lua/lua_fs.c +++ b/security/lua/lua_fs.c @@ -211,7 +211,7 @@ static int fs_file_fmode(lua_State *L) int top = lua_gettop(L); if (top >= 2) { int start = (top == 2) ? 2 : 3; - int and = lua_toboolean(L, 2); + int and = lua_isboolean(L, 2) && lua_toboolean(L, 2); fmode_t flags = tocflags(L, start, top, opts, 0); fmode_t res = file->f_mode & flags; lua_pushboolean(L, and ? res == flags : (int)res); From 7d48dee8091852711f6ff40b5ca74688e6a664b6 Mon Sep 17 00:00:00 2001 From: Jianye Gao Date: Wed, 17 Sep 2025 10:16:56 +0800 Subject: [PATCH 034/110] lua-lsm: api - add securebits --- security/lua/lua_kernel.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/security/lua/lua_kernel.c b/security/lua/lua_kernel.c index e32a3f1e627c..42acba8401ae 100644 --- a/security/lua/lua_kernel.c +++ b/security/lua/lua_kernel.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "lsm.h" #include "auxlib.h" #include "kvcache.h" @@ -92,12 +93,45 @@ static int kernel_cred_cap_ambient(lua_State *L) } } +/* + * table = cred:securebits() + * bool = cred:securebits('keep_caps') + * bool = cred:securebits(true, 'keep_caps', 'noroot') + */ +static int kernel_cred_securebits(lua_State *L) +{ + static const struct cflag_opt opts[] = { + { "noroot", SECBIT_NOROOT }, + { "no_setuid_fixup", SECBIT_NO_SETUID_FIXUP }, + { "keep_caps", SECBIT_KEEP_CAPS }, + { "no_cap_ambient_raise", SECBIT_NO_CAP_AMBIENT_RAISE }, + { "exec_restrict_file", SECBIT_EXEC_RESTRICT_FILE }, + { "exec_deny_interactive", SECBIT_EXEC_DENY_INTERACTIVE }, + { NULL, 0 } + }; + struct cred *cred = tocred(L, 1); + int top = lua_gettop(L); + if (top >= 2) { + int start = (top == 2) ? 2 : 3; + int and = lua_isboolean(L, 2) && lua_toboolean(L, 2); + unsigned int flags = tocflags(L, start, top, opts, 0); + unsigned int res = cred->securebits & flags; + lua_pushboolean(L, and ? res == flags : (int)res); + return 1; + } else if (top == 1) { + table_fromopts(L, opts, 0, (unsigned int)cred->securebits); + return 1; + } + return 0; +} + static const luaL_Reg cred_meth[] = { { "uids", kernel_cred_uids }, { "gids", kernel_cred_gids }, { "cap_eip", kernel_cred_cap_eip }, { "cap_bset", kernel_cred_cap_bset }, { "cap_ambient", kernel_cred_cap_ambient }, + { "securebits", kernel_cred_securebits }, { NULL, NULL } }; From 43f7d46060b591b36b5c411d18523f552949b2f9 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Mon, 15 Sep 2025 10:44:44 +0800 Subject: [PATCH 035/110] lua-lsm: add refcount.h --- security/lua/refcount.h | 44 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 security/lua/refcount.h diff --git a/security/lua/refcount.h b/security/lua/refcount.h new file mode 100644 index 000000000000..805ec526d809 --- /dev/null +++ b/security/lua/refcount.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Lua based LSM + * + * Copyright (C) 2025 The Alibaba Cloud Linux Authors. + */ + +#ifndef _SECURITY_LUA_LSM_REFCOUNT_H +#define _SECURITY_LUA_LSM_REFCOUNT_H + +#include "debug.h" +#include + +#define KASSERT(exp, msg) \ + do { \ + if (!(exp)) { \ + __log_err msg; \ + } \ + } while (0) + + +static inline void +refcount_init(atomic_t *count, long value) +{ + atomic_set(count, value); +} + +static inline void +refcount_acquire(atomic_t *count) +{ + KASSERT(atomic_read(count) < UINT_MAX, ("refcount %p overflowed", count)); + atomic_inc(count); +} + +static inline int +refcount_release(atomic_t *count) +{ + long n; + n = atomic_dec_return(count); + KASSERT(n >= 0, ("negative refcount %p", count)); + return (n == 0); +} + +#endif /* ! _SECURITY_LUA_LSM_REFCOUNT_H */ From a8e90c6c06035c689adedc10cd116a7885205a64 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Mon, 15 Sep 2025 17:08:17 +0800 Subject: [PATCH 036/110] lua-lsm: kvcache - Remove table type --- security/lua/kvcache.c | 290 -------------------------------------- security/lua/kvcache.h | 5 - security/lua/lua_object.h | 10 -- 3 files changed, 305 deletions(-) diff --git a/security/lua/kvcache.c b/security/lua/kvcache.c index ba5cf2fb8ff2..dc48583d4c81 100644 --- a/security/lua/kvcache.c +++ b/security/lua/kvcache.c @@ -90,7 +90,6 @@ static int kvcache_node_fill(lua_State *L, int idx, struct kvcache_node *node); static void kvcache_node_clear(struct kvcache_node *node) { - struct kvcache_node *n, *tmp; switch (node->tt) { case LUA_TBOOLEAN: case LUA_TNUMBER: @@ -99,14 +98,6 @@ static void kvcache_node_clear(struct kvcache_node *node) case LUA_TSTRING: kfree(node->s.s); break; - case LUA_TTABLE: - TAILQ_FOREACH_SAFE(n, &node->q.h, qlist, tmp) { - TAILQ_REMOVE(&node->q.h, n, qlist); - node->q.l -= 1; - kvcache_node_free(n); - } - WARN_ON(node->q.l != 0); - break; } node->tt = LUA_TNIL; } @@ -144,36 +135,9 @@ kvcache_module_unlink(struct kvcache_dict *dict, struct lua_module *module, } } -static int -kvcache_qnode_new(lua_State *L, int idx, struct kvcache_node *head, int left) -{ - struct kvcache_node *node; - int err; - - node = kvcache_value_alloc(); - if (node == NULL) - return -ENOMEM; - - err = kvcache_node_fill(L, idx, node); - if (err) { - kvcache_node_free(node); - return err; - } - - if (left) - TAILQ_INSERT_HEAD(&head->q.h, node, qlist); - else - TAILQ_INSERT_TAIL(&head->q.h, node, qlist); - - head->q.l += 1; - return 0; -} - static int kvcache_node_fill(lua_State *L, int idx, struct kvcache_node *node) { const char *s; - int tidx; - int err = 0; node->tt = lua_type(L, idx); switch (node->tt) { @@ -197,28 +161,6 @@ static int kvcache_node_fill(lua_State *L, int idx, struct kvcache_node *node) memcpy((void *)node->s.s, s, node->s.l + 1/* ending 0 */); break; - case LUA_TTABLE: - TAILQ_INIT(&node->q.h); - node->q.l = 0; - tidx = idx > 0 ? idx : idx - 1; - lua_pushnil(L); - while (lua_next(L, tidx) != 0) { - /* uses 'key' (at index -2) and 'value' (at index -1) */ - err = kvcache_qnode_new(L, -1, node, 0); - if (err) - break; - - /* removes 'value'; keeps 'key' for next iteration */ - lua_pop(L, 1); - } - if (err) { - kvcache_node_clear(node); - /* removes 'key' and 'value' */ - lua_pop(L, 2); - return err; - } - break; - default: return -EINVAL; } @@ -251,11 +193,6 @@ static int kvcache_node_refill(lua_State *L, int idx, struct kvcache_node *node) node->s.s = ntmp.s.s; node->s.l = ntmp.s.l; break; - case LUA_TTABLE: - TAILQ_INIT(&node->q.h); - TAILQ_SWAP(&node->q.h, &ntmp.q.h, kvcache_node, qlist); - node->q.l = ntmp.q.l; - break; } return 0; } @@ -312,9 +249,6 @@ kvcache_set(lua_State *L, struct kvcache_dict *dict, struct lua_module *module) static int kvcache_node_get(lua_State *L, struct kvcache_node *node) { - struct kvcache_node *n; - int idx = 1; - switch (node->tt) { case LUA_TBOOLEAN: lua_pushboolean(L, node->b); @@ -328,13 +262,6 @@ static int kvcache_node_get(lua_State *L, struct kvcache_node *node) case LUA_TSTRING: lua_pushlstring(L, node->s.s, node->s.l); break; - case LUA_TTABLE: - lua_createtable(L, node->q.l, 0); - TAILQ_FOREACH(n, &node->q.h, qlist) { - kvcache_node_get(L, n); - lua_rawseti(L, -2, idx++); - } - break; default: WARN_ON(1); return -EINVAL; @@ -400,133 +327,6 @@ kvcache_incr(lua_State *L, struct kvcache_dict *dict, struct lua_module *module) return kvcache_result(L, err, 1); } -static int -kvcache_qnode_append(lua_State *L, int from, struct kvcache_node *head, int left) -{ - int top = lua_gettop(L); - int nres = 0; - int err; - - for ( ; from <= top; from++) { - err = kvcache_qnode_new(L, from, head, left); - if (err) { - __log_err("qnode_new: from = %d, err = %d\n", from, err); - break; - } - nres += 1; - } - return nres; -} - -static int kvcache_push(lua_State *L, struct kvcache_dict *dict, - struct lua_module *module, int left) -{ - size_t len; - const char *key = luaL_checklstring(L, 2, &len); - struct kvcache_node *node; - int nres; - int err = 0; - - write_lock(&dict->lock); - node = kvcache_lookup(dict, module, key); - if (node) { - if (node->tt == LUA_TTABLE) { - nres = kvcache_qnode_append(L, 3, node, left); - lua_pushinteger(L, nres); - } else { - err = -EINVAL; - } - goto unlock; - } - - err = -ERANGE; - if (atomic_read(&dict->count) >= dict->capacity) - goto unlock; - - err = -ENOMEM; - node = kvcache_node_alloc(dict, module, key, len); - if (node == NULL) - goto unlock; - - node->tt = LUA_TTABLE; - node->q.l = 0; - TAILQ_INIT(&node->q.h); - nres = kvcache_qnode_append(L, 3, node, left); - - kvcache_module_link(dict, module, node); - - lua_pushinteger(L, nres); - err = 0; - -unlock: - write_unlock(&dict->lock); - - return kvcache_result(L, err, 1); -} - -static int -kvcache_qnode_pop(lua_State *L, int npop, struct kvcache_node *head, int left) -{ - struct kvcache_node *node; - int nres = 0; - - while (head->q.l > 0 && npop-- > 0) { - WARN_ON(TAILQ_EMPTY(&head->q.h)); - - if (left) - node = TAILQ_FIRST(&head->q.h); - else - node = TAILQ_LAST_FAST(&head->q.h, kvcache_node, qlist); - - TAILQ_REMOVE(&head->q.h, node, qlist); - head->q.l -= 1; - kvcache_node_get(L, node); - kvcache_node_free(node); - nres += 1; - } - return nres; -} - -static int kvcache_pop(lua_State *L, struct kvcache_dict *dict, - struct lua_module *module, int left) -{ - const char *key = luaL_checkstring(L, 2); - int npop = luaL_optint(L, 3, 1); - struct kvcache_node *node; - int nres = 0; - int err = 0; - - write_lock(&dict->lock); - node = kvcache_lookup(dict, module, key); - if (node) { - if (node->tt == LUA_TTABLE) - nres = kvcache_qnode_pop(L, npop, node, left); - else - err = -EINVAL; - } - write_unlock(&dict->lock); - return kvcache_result(L, err, nres); -} - -static int kvcache_llen(lua_State *L, struct kvcache_dict *dict, - struct lua_module *module) -{ - const char *key = luaL_checkstring(L, 2); - struct kvcache_node *node; - int err = 0; - - read_lock(&dict->lock); - node = kvcache_lookup(dict, module, key); - if (node) { - if (node->tt == LUA_TTABLE) - lua_pushinteger(L, node->q.l); - else - err = -EINVAL; - } - read_unlock(&dict->lock); - return kvcache_result(L, err, 1); -} - void kvcache_module_nodes_gc(struct lua_module *module) { TAILQ_HEAD(, kvcache_node) cleanup_list; @@ -631,61 +431,6 @@ int lua_object_incr(lua_State *L, struct kvcache_dict *dict) return kvcache_incr(L, dict, module); } -int lua_object_lpush(lua_State *L, struct kvcache_dict *dict) -{ - struct lua_module *module; - - module = module_from_object_fenv(L, 1); - if (module == NULL) - return kvcache_result(L, -ENOENT, 0); - - return kvcache_push(L, dict, module, 1); -} - -int lua_object_rpush(lua_State *L, struct kvcache_dict *dict) -{ - struct lua_module *module; - - module = module_from_object_fenv(L, 1); - if (module == NULL) - return kvcache_result(L, -ENOENT, 0); - - return kvcache_push(L, dict, module, 0); -} - -int lua_object_lpop(lua_State *L, struct kvcache_dict *dict) -{ - struct lua_module *module; - - module = module_from_object_fenv(L, 1); - if (module == NULL) - return kvcache_result(L, -ENOENT, 0); - - return kvcache_pop(L, dict, module, 1); -} - -int lua_object_rpop(lua_State *L, struct kvcache_dict *dict) -{ - struct lua_module *module; - - module = module_from_object_fenv(L, 1); - if (module == NULL) - return kvcache_result(L, -ENOENT, 0); - - return kvcache_pop(L, dict, module, 0); -} - -int lua_object_llen(lua_State *L, struct kvcache_dict *dict) -{ - struct lua_module *module; - - module = module_from_object_fenv(L, 1); - if (module == NULL) - return kvcache_result(L, -ENOENT, 0); - - return kvcache_llen(L, dict, module); -} - /* * __index = function(object, key) * local mt = getmetatable(object) @@ -750,36 +495,6 @@ static int shdict_incr(lua_State *L) return kvcache_incr(L, shdict, NULL); } -static int shdict_lpush(lua_State *L) -{ - struct kvcache_dict *shdict = toshdict(L, 1); - return kvcache_push(L, shdict, NULL, 1); -} - -static int shdict_rpush(lua_State *L) -{ - struct kvcache_dict *shdict = toshdict(L, 1); - return kvcache_push(L, shdict, NULL, 0); -} - -static int shdict_lpop(lua_State *L) -{ - struct kvcache_dict *shdict = toshdict(L, 1); - return kvcache_pop(L, shdict, NULL, 1); -} - -static int shdict_rpop(lua_State *L) -{ - struct kvcache_dict *shdict = toshdict(L, 1); - return kvcache_pop(L, shdict, NULL, 0); -} - -static int shdict_llen(lua_State *L) -{ - struct kvcache_dict *shdict = toshdict(L, 1); - return kvcache_llen(L, shdict, NULL); -} - static int shdict_index(lua_State *L) { struct kvcache_dict *shdict = toshdict(L, 1); @@ -817,11 +532,6 @@ static const luaL_Reg shdict_meth[] = { { "set", shdict_set }, { "get", shdict_get }, { "incr", shdict_incr }, - { "lpush", shdict_lpush }, - { "rpush", shdict_rpush }, - { "lpop", shdict_lpop }, - { "rpop", shdict_rpop }, - { "llen", shdict_llen }, { "__index", shdict_index }, { "__newindex", shdict_set }, { "__tostring", shdict_tostring }, diff --git a/security/lua/kvcache.h b/security/lua/kvcache.h index 0063f992be84..1fa22e0f474e 100644 --- a/security/lua/kvcache.h +++ b/security/lua/kvcache.h @@ -65,11 +65,6 @@ extern const int _module_sentinel; int lua_object_get(lua_State *L, struct kvcache_dict *dict); int lua_object_incr(lua_State *L, struct kvcache_dict *dict); -int lua_object_lpush(lua_State *L, struct kvcache_dict *dict); -int lua_object_rpush(lua_State *L, struct kvcache_dict *dict); -int lua_object_lpop(lua_State *L, struct kvcache_dict *dict); -int lua_object_rpop(lua_State *L, struct kvcache_dict *dict); -int lua_object_llen(lua_State *L, struct kvcache_dict *dict); int lua_object_index(lua_State *L, struct kvcache_dict *dict); int lua_object_newindex(lua_State *L, struct kvcache_dict *dict); diff --git a/security/lua/lua_object.h b/security/lua/lua_object.h index 838fcb89fb99..ceb93a41bdef 100644 --- a/security/lua/lua_object.h +++ b/security/lua/lua_object.h @@ -52,11 +52,6 @@ LUA_OBJECT_DEFINE(class, name, ctype, d) \ LUA_OBJECT_KVCACHE_FUNC(class, name, ctype, blob, kvcache_get, get) \ LUA_OBJECT_KVCACHE_FUNC(class, name, ctype, blob, kvcache_incr, incr) \ - LUA_OBJECT_KVCACHE_FUNC(class, name, ctype, blob, kvcache_lpush, lpush) \ - LUA_OBJECT_KVCACHE_FUNC(class, name, ctype, blob, kvcache_rpush, rpush) \ - LUA_OBJECT_KVCACHE_FUNC(class, name, ctype, blob, kvcache_lpop, lpop) \ - LUA_OBJECT_KVCACHE_FUNC(class, name, ctype, blob, kvcache_rpop, rpop) \ - LUA_OBJECT_KVCACHE_FUNC(class, name, ctype, blob, kvcache_llen, llen) \ LUA_OBJECT_KVCACHE_FUNC(class, name, ctype, blob, index, index) \ LUA_OBJECT_KVCACHE_FUNC(class, name, ctype, blob, newindex, newindex) \ static inline void create_ ## name ## _meta(lua_State *L, \ @@ -66,11 +61,6 @@ { "kvcache_set", class ## _ ## name ## _newindex }, \ { "kvcache_get", class ## _ ## name ## _kvcache_get }, \ { "kvcache_incr", class ## _ ## name ## _kvcache_incr }, \ - { "kvcache_lpush", class ## _ ## name ## _kvcache_lpush }, \ - { "kvcache_rpush", class ## _ ## name ## _kvcache_rpush }, \ - { "kvcache_lpop", class ## _ ## name ## _kvcache_lpop }, \ - { "kvcache_rpop", class ## _ ## name ## _kvcache_rpop }, \ - { "kvcache_llen", class ## _ ## name ## _kvcache_llen }, \ { "__index", class ## _ ## name ## _index }, \ { "__newindex", class ## _ ## name ## _newindex }, \ { "__tostring", class ## _ ## name ## _tostring }, \ From e9ce6495ce33660f82301dd257096a80ccccfa75 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Mon, 15 Sep 2025 19:50:24 +0800 Subject: [PATCH 037/110] lua-lsm: kvcache - lock on node --- security/lua/kvcache.c | 241 +++++++++++++++++++++++++++-------------- security/lua/kvcache.h | 3 + 2 files changed, 162 insertions(+), 82 deletions(-) diff --git a/security/lua/kvcache.c b/security/lua/kvcache.c index dc48583d4c81..d7bf7ba01945 100644 --- a/security/lua/kvcache.c +++ b/security/lua/kvcache.c @@ -41,22 +41,13 @@ static int kvcache_result(lua_State *L, int err, int nresults) case -EINVAL: error = "invalid value"; break; case -ERANGE: error = "no space"; break; case -ENOENT: error = "no module"; break; + case -EEXIST: error = "exists"; break; } lua_pushnil(L); lua_pushstring(L, error); return 2; } -static struct kvcache_node * -kvcache_lookup(struct kvcache_dict *dict, - struct lua_module *module, const char *key) -{ - struct kvcache_node tmp; - tmp.key = key; - tmp.module = module; - return RB_FIND(kvcache, &dict->root, &tmp); -} - static struct kvcache_node * kvcache_node_alloc(struct kvcache_dict *dict, struct lua_module *module, const char *key, size_t len) @@ -79,6 +70,8 @@ kvcache_node_alloc(struct kvcache_dict *dict, struct lua_module *module, node->tt = LUA_TNIL; node->module = module; node->dict = dict; + refcount_init(&node->refcount, 1); + rwlock_init(&node->lock); atomic_inc(&node_nalloc); return node; } @@ -109,22 +102,67 @@ static void kvcache_node_free(struct kvcache_node *node) kfree(node); } -static void -kvcache_module_link(struct kvcache_dict *dict, struct lua_module *module, - struct kvcache_node *node) +static void kvcache_node_hold(struct kvcache_node *node) { - RB_INSERT(kvcache, &dict->root, node); - atomic_inc(&dict->count); - if (module) { - spin_lock(&module->kvnodes_lock); - TAILQ_INSERT_TAIL(&module->kvnodes, node, modlist); - spin_unlock(&module->kvnodes_lock); + if (node) + refcount_acquire(&node->refcount); +} + +static void kvcache_node_drop(struct kvcache_node *node) +{ + if (node == NULL) + return; + if (refcount_release(&node->refcount)) + kvcache_node_free(node); +} + +static struct kvcache_node * +kvcache_lookup(struct kvcache_dict *dict, + struct lua_module *module, const char *key) +{ + struct kvcache_node tmp, *node; + tmp.key = key; + tmp.module = module; + read_lock(&dict->lock); + node = RB_FIND(kvcache, &dict->root, &tmp); + kvcache_node_hold(node); + read_unlock(&dict->lock); + return node; +} + +static struct kvcache_node * +kvcache_module_link(struct kvcache_dict *dict, + struct lua_module *module, struct kvcache_node *node) +{ + struct kvcache_node *prev; + + write_lock(&dict->lock); + + if (atomic_read(&dict->count) >= dict->capacity) { + prev = ERR_PTR(-ERANGE); + goto unlock; } + + prev = RB_INSERT(kvcache, &dict->root, node); + if (prev) { + kvcache_node_hold(prev); + } else { + atomic_inc(&dict->count); + if (module) { + spin_lock(&module->kvnodes_lock); + TAILQ_INSERT_TAIL(&module->kvnodes, node, modlist); + spin_unlock(&module->kvnodes_lock); + } + } + +unlock: + write_unlock(&dict->lock); + return prev; } static void -kvcache_module_unlink(struct kvcache_dict *dict, struct lua_module *module, - struct kvcache_node *node) +kvcache_module_unlink_unlocked(struct kvcache_dict *dict, + struct lua_module *module, struct kvcache_node *node) { RB_REMOVE(kvcache, &dict->root, node); atomic_dec(&dict->count); @@ -135,6 +173,15 @@ kvcache_module_unlink(struct kvcache_dict *dict, struct lua_module *module, } } +static void +kvcache_module_unlink(struct kvcache_dict *dict, + struct lua_module *module, struct kvcache_node *node) +{ + write_lock(&dict->lock); + kvcache_module_unlink_unlocked(dict, module, node); + write_unlock(&dict->lock); +} + static int kvcache_node_fill(lua_State *L, int idx, struct kvcache_node *node) { const char *s; @@ -167,6 +214,27 @@ static int kvcache_node_fill(lua_State *L, int idx, struct kvcache_node *node) return 0; } +static void +kvcache_node_copy(struct kvcache_node *node, struct kvcache_node *src) +{ + node->tt = src->tt; + switch (src->tt) { + case LUA_TBOOLEAN: + node->b = src->b; + break; + case LUA_TNUMBER: + node->n = src->n; + break; + case LUA_TLIGHTUSERDATA: + node->p = src->p; + break; + case LUA_TSTRING: + node->s.s = src->s.s; + node->s.l = src->s.l; + break; + } +} + static int kvcache_node_refill(lua_State *L, int idx, struct kvcache_node *node) { struct kvcache_node ntmp; @@ -176,24 +244,11 @@ static int kvcache_node_refill(lua_State *L, int idx, struct kvcache_node *node) if (err) return err; + write_lock(&node->lock); kvcache_node_clear(node); + kvcache_node_copy(node, &ntmp); + write_unlock(&node->lock); - node->tt = ntmp.tt; - switch (node->tt) { - case LUA_TBOOLEAN: - node->b = ntmp.b; - break; - case LUA_TNUMBER: - node->n = ntmp.n; - break; - case LUA_TLIGHTUSERDATA: - node->p = ntmp.p; - break; - case LUA_TSTRING: - node->s.s = ntmp.s.s; - node->s.l = ntmp.s.l; - break; - } return 0; } @@ -203,44 +258,50 @@ kvcache_set(lua_State *L, struct kvcache_dict *dict, struct lua_module *module) size_t len; const char *key = luaL_checklstring(L, 2, &len); int tt = lua_type(L, 3); - struct kvcache_node *node; + struct kvcache_node *node, *prev; int err = 0; - write_lock(&dict->lock); node = kvcache_lookup(dict, module, key); if (node) { if (tt == LUA_TNIL) { kvcache_module_unlink(dict, module, node); - kvcache_node_free(node); + kvcache_node_drop(node); } else { err = kvcache_node_refill(L, 3, node); } - goto unlock; + kvcache_node_drop(node); + goto ret; } if (tt == LUA_TNIL) - goto unlock; - - err = -ERANGE; - if (atomic_read(&dict->count) >= dict->capacity) - goto unlock; + goto ret; err = -ENOMEM; node = kvcache_node_alloc(dict, module, key, len); if (node == NULL) - goto unlock; + goto ret; err = kvcache_node_fill(L, 3, node); if (err) { kvcache_node_free(node); - goto unlock; + goto ret; } - kvcache_module_link(dict, module, node); + err = -EEXIST; + prev = kvcache_module_link(dict, module, node); + if (prev) { + if (IS_ERR(prev)) + err = PTR_ERR(prev); + else + kvcache_node_drop(prev); -unlock: - write_unlock(&dict->lock); + kvcache_node_free(node); + goto ret; + } + err = 0; + +ret: if (!err) lua_pushboolean(L, 1); @@ -249,18 +310,24 @@ kvcache_set(lua_State *L, struct kvcache_dict *dict, struct lua_module *module) static int kvcache_node_get(lua_State *L, struct kvcache_node *node) { - switch (node->tt) { + struct kvcache_node ntmp; + + read_lock(&node->lock); + kvcache_node_copy(&ntmp, node); + read_unlock(&node->lock); + + switch (ntmp.tt) { case LUA_TBOOLEAN: - lua_pushboolean(L, node->b); + lua_pushboolean(L, ntmp.b); break; case LUA_TNUMBER: - lua_pushnumber(L, node->n); + lua_pushnumber(L, ntmp.n); break; case LUA_TLIGHTUSERDATA: - lua_pushlightuserdata(L, node->p); + lua_pushlightuserdata(L, ntmp.p); break; case LUA_TSTRING: - lua_pushlstring(L, node->s.s, node->s.l); + lua_pushlstring(L, ntmp.s.s, ntmp.s.l); break; default: WARN_ON(1); @@ -275,13 +342,13 @@ kvcache_get(lua_State *L, struct kvcache_dict *dict, struct lua_module *module) const char *key = luaL_checkstring(L, 2); struct kvcache_node *node; - read_lock(&dict->lock); node = kvcache_lookup(dict, module, key); - if (node) + if (node) { kvcache_node_get(L, node); - else + kvcache_node_drop(node); + } else { lua_pushnil(L); - read_unlock(&dict->lock); + } return 1; } @@ -291,38 +358,48 @@ kvcache_incr(lua_State *L, struct kvcache_dict *dict, struct lua_module *module) size_t len; const char *key = luaL_checklstring(L, 2, &len); lua_Number n = luaL_optnumber(L, 3, 1); - struct kvcache_node *node; + struct kvcache_node *node, *prev; int err = 0; - write_lock(&dict->lock); node = kvcache_lookup(dict, module, key); if (node) { + write_lock(&node->lock); if (node->tt == LUA_TNUMBER) { node->n += n; - lua_pushnumber(L, node->n); + n = node->n; } else { err = -EINVAL; } - goto unlock; + write_unlock(&node->lock); + kvcache_node_drop(node); + goto ret; } - err = -ERANGE; - if (atomic_read(&dict->count) >= dict->capacity) - goto unlock; - err = -ENOMEM; node = kvcache_node_alloc(dict, module, key, len); if (node == NULL) - goto unlock; + goto ret; node->n = n; node->tt = LUA_TNUMBER; - kvcache_module_link(dict, module, node); - lua_pushnumber(L, node->n); + + err = -EEXIST; + prev = kvcache_module_link(dict, module, node); + if (prev) { + if (IS_ERR(prev)) + err = PTR_ERR(prev); + else + kvcache_node_drop(prev); + + kvcache_node_free(node); + goto ret; + } + err = 0; -unlock: - write_unlock(&dict->lock); +ret: + if (!err) + lua_pushnumber(L, n); return kvcache_result(L, err, 1); } @@ -340,13 +417,13 @@ void kvcache_module_nodes_gc(struct lua_module *module) TAILQ_FOREACH_SAFE(node, &cleanup_list, modlist, tmp) { struct kvcache_dict *dict = node->dict; - WARN_ON(!dict); - write_lock(&dict->lock); - RB_REMOVE(kvcache, &dict->root, node); - write_unlock(&dict->lock); - atomic_dec(&dict->count); - - kvcache_node_free(node); + BUG_ON(!dict); + /* + * module is set to NULL, so there is no need to remove + * node from the module kvnodes queue. + */ + kvcache_module_unlink(dict, NULL, node); + kvcache_node_drop(node); } } @@ -356,8 +433,8 @@ void kvcache_dict_free(struct kvcache_dict *dict) write_lock(&dict->lock); RB_FOREACH_SAFE(node, kvcache, &dict->root, n) { - kvcache_module_unlink(dict, node->module, node); - kvcache_node_free(node); + kvcache_module_unlink_unlocked(dict, node->module, node); + kvcache_node_drop(node); } WARN_ON(atomic_read(&dict->count) != 0); write_unlock(&dict->lock); diff --git a/security/lua/kvcache.h b/security/lua/kvcache.h index 1fa22e0f474e..eaa06924dfaa 100644 --- a/security/lua/kvcache.h +++ b/security/lua/kvcache.h @@ -10,6 +10,7 @@ #include #include +#include "refcount.h" #undef LIST_HEAD #include "queue.h" #undef RB_ROOT @@ -23,6 +24,8 @@ struct kvcache_node { const char *key; struct lua_module *module; struct kvcache_dict *dict; + atomic_t refcount; + rwlock_t lock; int tt; union { int b; From 124e21caa6eda63c46a5100c8695e34b2da97a72 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Mon, 15 Sep 2025 20:12:27 +0800 Subject: [PATCH 038/110] lua-lsm: kvcache - Remove string type Unlike boolean and number values, string types require additional memory allocation. Avoid memory allocations whenever possible within the atomic context of locked nodes. --- security/lua/kvcache.c | 34 +++++----------------------------- security/lua/kvcache.h | 17 ++--------------- 2 files changed, 7 insertions(+), 44 deletions(-) diff --git a/security/lua/kvcache.c b/security/lua/kvcache.c index d7bf7ba01945..df5371555cb2 100644 --- a/security/lua/kvcache.c +++ b/security/lua/kvcache.c @@ -40,7 +40,7 @@ static int kvcache_result(lua_State *L, int err, int nresults) case -ENOMEM: error = "no memory"; break; case -EINVAL: error = "invalid value"; break; case -ERANGE: error = "no space"; break; - case -ENOENT: error = "no module"; break; + case -ESRCH: error = "no module"; break; case -EEXIST: error = "exists"; break; } lua_pushnil(L); @@ -55,8 +55,9 @@ kvcache_node_alloc(struct kvcache_dict *dict, struct lua_module *module, size_t l = sizeof(struct kvcache_node); struct kvcache_node *node; + WARN_ON(in_atomic()); l += key ? (len + 1) : 0; - node = kmalloc(l, GFP_ATOMIC); + node = kmalloc(l, GFP_NOFS); if (node == NULL) return NULL; @@ -76,11 +77,6 @@ kvcache_node_alloc(struct kvcache_dict *dict, struct lua_module *module, return node; } -#define kvcache_value_alloc() kvcache_node_alloc(NULL, NULL, NULL, 0) - -static void kvcache_node_free(struct kvcache_node *node); -static int kvcache_node_fill(lua_State *L, int idx, struct kvcache_node *node); - static void kvcache_node_clear(struct kvcache_node *node) { switch (node->tt) { @@ -88,9 +84,6 @@ static void kvcache_node_clear(struct kvcache_node *node) case LUA_TNUMBER: case LUA_TLIGHTUSERDATA: break; - case LUA_TSTRING: - kfree(node->s.s); - break; } node->tt = LUA_TNIL; } @@ -184,8 +177,6 @@ kvcache_module_unlink(struct kvcache_dict *dict, static int kvcache_node_fill(lua_State *L, int idx, struct kvcache_node *node) { - const char *s; - node->tt = lua_type(L, idx); switch (node->tt) { case LUA_TBOOLEAN: @@ -200,14 +191,6 @@ static int kvcache_node_fill(lua_State *L, int idx, struct kvcache_node *node) node->p = lua_touserdata(L, idx); break; - case LUA_TSTRING: - s = lua_tolstring(L, idx, &node->s.l); - node->s.s = kmalloc(node->s.l + 1, GFP_ATOMIC); - if (node->s.s == NULL) - return -ENOMEM; - memcpy((void *)node->s.s, s, node->s.l + 1/* ending 0 */); - break; - default: return -EINVAL; } @@ -228,10 +211,6 @@ kvcache_node_copy(struct kvcache_node *node, struct kvcache_node *src) case LUA_TLIGHTUSERDATA: node->p = src->p; break; - case LUA_TSTRING: - node->s.s = src->s.s; - node->s.l = src->s.l; - break; } } @@ -326,9 +305,6 @@ static int kvcache_node_get(lua_State *L, struct kvcache_node *node) case LUA_TLIGHTUSERDATA: lua_pushlightuserdata(L, ntmp.p); break; - case LUA_TSTRING: - lua_pushlstring(L, ntmp.s.s, ntmp.s.l); - break; default: WARN_ON(1); return -EINVAL; @@ -503,7 +479,7 @@ int lua_object_incr(lua_State *L, struct kvcache_dict *dict) module = module_from_object_fenv(L, 1); if (module == NULL) - return kvcache_result(L, -ENOENT, 0); + return kvcache_result(L, -ESRCH, 0); return kvcache_incr(L, dict, module); } @@ -548,7 +524,7 @@ int lua_object_newindex(lua_State *L, struct kvcache_dict *dict) module = module_from_object_fenv(L, 1); if (module == NULL) - return kvcache_result(L, -ENOENT, 0); + return kvcache_result(L, -ESRCH, 0); return kvcache_set(L, dict, module); } diff --git a/security/lua/kvcache.h b/security/lua/kvcache.h index eaa06924dfaa..1cb598268500 100644 --- a/security/lua/kvcache.h +++ b/security/lua/kvcache.h @@ -31,22 +31,9 @@ struct kvcache_node { int b; lua_Number n; void *p; - struct { - const char *s; - size_t l; - } s; - struct { - TAILQ_HEAD(, kvcache_node) h; - size_t l; - } q; - }; - union { - struct { - RB_ENTRY(kvcache_node) node; - TAILQ_ENTRY(kvcache_node) modlist; - }; - TAILQ_ENTRY(kvcache_node) qlist; }; + RB_ENTRY(kvcache_node) node; + TAILQ_ENTRY(kvcache_node) modlist; }; struct kvcache_dict { From 71dfc5daecad163800f36ca42fc3650f4a4fe0f1 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Tue, 16 Sep 2025 15:53:56 +0800 Subject: [PATCH 039/110] lua-lsm: replace tailq with linux list --- security/lua/kvcache.c | 12 ++++++------ security/lua/kvcache.h | 5 ++--- security/lua/lsm.c | 26 +++++++++++++------------- security/lua/lsm.h | 18 +++++++----------- security/lua/lsm_defs.h | 2 +- 5 files changed, 29 insertions(+), 34 deletions(-) diff --git a/security/lua/kvcache.c b/security/lua/kvcache.c index df5371555cb2..82f5cad5c4a2 100644 --- a/security/lua/kvcache.c +++ b/security/lua/kvcache.c @@ -143,7 +143,7 @@ kvcache_module_link(struct kvcache_dict *dict, atomic_inc(&dict->count); if (module) { spin_lock(&module->kvnodes_lock); - TAILQ_INSERT_TAIL(&module->kvnodes, node, modlist); + list_add_tail(&node->modlist, &module->kvnodes); spin_unlock(&module->kvnodes_lock); } } @@ -161,7 +161,7 @@ kvcache_module_unlink_unlocked(struct kvcache_dict *dict, atomic_dec(&dict->count); if (module) { spin_lock(&module->kvnodes_lock); - TAILQ_REMOVE(&module->kvnodes, node, modlist); + list_del(&node->modlist); spin_unlock(&module->kvnodes_lock); } } @@ -382,15 +382,15 @@ kvcache_incr(lua_State *L, struct kvcache_dict *dict, struct lua_module *module) void kvcache_module_nodes_gc(struct lua_module *module) { - TAILQ_HEAD(, kvcache_node) cleanup_list; + struct list_head cleanup_list; struct kvcache_node *node, *tmp; - TAILQ_INIT(&cleanup_list); + INIT_LIST_HEAD(&cleanup_list); spin_lock(&module->kvnodes_lock); - TAILQ_SWAP(&module->kvnodes, &cleanup_list, kvcache_node, modlist); + list_cut_position(&cleanup_list, &module->kvnodes, module->kvnodes.next); spin_unlock(&module->kvnodes_lock); - TAILQ_FOREACH_SAFE(node, &cleanup_list, modlist, tmp) { + list_for_each_entry_safe(node, tmp, &cleanup_list, modlist) { struct kvcache_dict *dict = node->dict; BUG_ON(!dict); diff --git a/security/lua/kvcache.h b/security/lua/kvcache.h index 1cb598268500..a6de1196b081 100644 --- a/security/lua/kvcache.h +++ b/security/lua/kvcache.h @@ -8,11 +8,10 @@ #ifndef _SECURITY_LUA_LSM_KVCACHE_H #define _SECURITY_LUA_LSM_KVCACHE_H +#include #include #include #include "refcount.h" -#undef LIST_HEAD -#include "queue.h" #undef RB_ROOT #include "tree.h" @@ -33,7 +32,7 @@ struct kvcache_node { void *p; }; RB_ENTRY(kvcache_node) node; - TAILQ_ENTRY(kvcache_node) modlist; + struct list_head modlist; }; struct kvcache_dict { diff --git a/security/lua/lsm.c b/security/lua/lsm.c index ed83e440b349..6b2e64453b3a 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -46,7 +46,7 @@ int lua_lsm_initialized __initdata; /********************************* lsm hook *********************************/ -struct lua_modules_head lsm_modules = TAILQ_HEAD_INITIALIZER(lsm_modules); +struct list_head lsm_modules = LIST_HEAD_INIT(lsm_modules); DEFINE_RWLOCK(modules_lock); struct lua_lsm_hook_stat lua_lsm_hook_stats[] = { @@ -91,7 +91,7 @@ static int lua_shared_index(lua_State *L) module = lua_touserdata(L, -1); write_lock(&module->shdict_lock); - TAILQ_FOREACH(shdict, &module->shdict, list) { + list_for_each_entry(shdict, &module->shdicts, list) { if (strcmp(shdict->name, name) == 0) break; } @@ -104,7 +104,7 @@ static int lua_shared_index(lua_State *L) goto err_free; kvcache_dict_init(&shdict->dict); atomic_inc(&module->shdict_count); - TAILQ_INSERT_TAIL(&module->shdict, shdict, list); + list_add_tail(&shdict->list, &module->shdicts); } write_unlock(&module->shdict_lock); @@ -215,7 +215,7 @@ static int lua_module_index(lua_State *L) int err; /* module queries are always run with a read lock */ - TAILQ_FOREACH(module, &lsm_modules, list) { + list_for_each_entry(module, &lsm_modules, list) { if (strcmp(module->name, key) != 0) continue; @@ -522,20 +522,20 @@ int lua_module_register(const char *code, size_t len) memcpy(module->chunk, chunk, chunk_len); module->chunk_len = chunk_len; - TAILQ_INIT(&module->shdict); + INIT_LIST_HEAD(&module->shdicts); rwlock_init(&module->shdict_lock); atomic_set(&module->shdict_count, 0); - TAILQ_INIT(&module->kvnodes); + INIT_LIST_HEAD(&module->kvnodes); spin_lock_init(&module->kvnodes_lock); write_lock_bh(&modules_lock); - TAILQ_FOREACH(m, &lsm_modules, list) { + list_for_each_entry(m, &lsm_modules, list) { if (strcmp(module->name, m->name) == 0) break; } if (m == NULL) { - TAILQ_INSERT_TAIL(&lsm_modules, module, list); + list_add_tail(&module->list, &lsm_modules); for (i = 0; lua_lsm_hook_stats[i].name; i++) { if (__BITMAP_ISSET(i, &module->hookfuncs)) @@ -595,9 +595,9 @@ int lua_module_unregister(const char *name) int i; write_lock_bh(&modules_lock); - TAILQ_FOREACH_SAFE(module, &lsm_modules, list, tmp) { + list_for_each_entry_safe(module, tmp, &lsm_modules, list) { if (strcmp(module->name, name) == 0) { - TAILQ_REMOVE(&lsm_modules, module, list); + list_del(&module->list); break; } } @@ -614,8 +614,8 @@ int lua_module_unregister(const char *name) atomic_dec(&lua_lsm_hook_stats[i].nhooks); } - TAILQ_FOREACH_SAFE(shdict, &module->shdict, list, shdict_tmp) { - TAILQ_REMOVE(&module->shdict, shdict, list); + list_for_each_entry_safe(shdict, shdict_tmp, &module->shdicts, list) { + list_del(&shdict->list); kvcache_dict_free(&shdict->dict); kfree(shdict->name); kfree(shdict); @@ -667,7 +667,7 @@ int modules_show(struct seq_file *m, void *v) seq_printf(m, "%s\n", TABLINE); read_lock_bh(&modules_lock); - TAILQ_FOREACH(module, &lsm_modules, list) { + list_for_each_entry(module, &lsm_modules, list) { seq_printf(m, "%-12s %-10s %6zu %6d %-48s\n", module->name, module->license, module->chunk_len, module->nhooks, module->author); diff --git a/security/lua/lsm.h b/security/lua/lsm.h index 10219cba8806..96a85d0a187f 100644 --- a/security/lua/lsm.h +++ b/security/lua/lsm.h @@ -8,16 +8,15 @@ #ifndef _SECURITY_LUA_LSM_LSM_H #define _SECURITY_LUA_LSM_LSM_H +#include #include #include #include #include #include -#include #include +#include #include "bitmap.h" -#undef LIST_HEAD -#include "queue.h" #include "kvcache.h" /* Flag indicating whether initialization completed */ @@ -29,7 +28,6 @@ extern int lua_lsm_initialized __initdata; struct lua_lsm_hook_stat { const char *name; atomic_t nhooks; - TAILQ_ENTRY(lua_lsm_hook_stat) list; #ifdef CONFIG_SECURITY_LUA_LSM_STATS atomic_t count; atomic64_t time; /* ns */ @@ -56,7 +54,7 @@ enum { }; struct lua_module_shdict { - TAILQ_ENTRY(lua_module_shdict) list; + struct list_head list; const char *name; struct kvcache_dict dict; }; @@ -67,21 +65,19 @@ struct lua_module { const char *description; const char *license; int version; - TAILQ_ENTRY(lua_module) list; + struct list_head list; __BITMAP_TYPE(, uint32_t, __LL_NR_MAX) hookfuncs; int nhooks; char *chunk; size_t chunk_len; - TAILQ_HEAD(, lua_module_shdict) shdict; + struct list_head shdicts; rwlock_t shdict_lock; atomic_t shdict_count; - TAILQ_HEAD(, kvcache_node) kvnodes; + struct list_head kvnodes; spinlock_t kvnodes_lock; }; -TAILQ_HEAD(lua_modules_head, lua_module); - -extern struct lua_modules_head lsm_modules; +extern struct list_head lsm_modules; extern rwlock_t modules_lock; diff --git a/security/lua/lsm_defs.h b/security/lua/lsm_defs.h index 66f19ee9f9a1..c5330646f7d3 100644 --- a/security/lua/lsm_defs.h +++ b/security/lua/lsm_defs.h @@ -132,7 +132,7 @@ lua_getfenv(L, -1); /* save thread.fenv */ \ lua_getfield(L, LUA_REGISTRYINDEX, "_MODULES"); \ /* stack: [traceback, thread, env, _MODULES] */ \ - TAILQ_FOREACH(module, &lsm_modules, list) { \ + list_for_each_entry(module, &lsm_modules, list) { \ if (!__BITMAP_ISSET(__LL_NR_ ## NAME, &module->hookfuncs)) \ continue; \ lua_getfield(L, -1, module->name); \ From d37570c801990ccb06fd69010710c2e9e51e4577 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Wed, 17 Sep 2025 14:22:17 +0800 Subject: [PATCH 040/110] lua-lsm: lockess modules with srcu --- security/lua/lsm.c | 99 +++++++++++++++++++++++------------------ security/lua/lsm.h | 2 +- security/lua/lsm_defs.h | 8 ++-- 3 files changed, 62 insertions(+), 47 deletions(-) diff --git a/security/lua/lsm.c b/security/lua/lsm.c index 6b2e64453b3a..bb70cb1821c0 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -47,7 +47,8 @@ int lua_lsm_initialized __initdata; /********************************* lsm hook *********************************/ struct list_head lsm_modules = LIST_HEAD_INIT(lsm_modules); -DEFINE_RWLOCK(modules_lock); +static DEFINE_SPINLOCK(modules_lock); +DEFINE_SRCU(modules_ss); struct lua_lsm_hook_stat lua_lsm_hook_stats[] = { #define LSM_HOOK(RET, DEFAULT, NAME, ...) \ @@ -78,6 +79,7 @@ static int lua_shared_index(lua_State *L) const char *name = luaL_checkstring(L, 2); struct lua_module_shdict *shdict; struct lua_module *module; + int found = 0; __log_info("READ shared table, [%s] %s\n", luaL_typename(L, 2), lua_tostring(L, 2) ?: "(null)"); @@ -92,10 +94,12 @@ static int lua_shared_index(lua_State *L) write_lock(&module->shdict_lock); list_for_each_entry(shdict, &module->shdicts, list) { - if (strcmp(shdict->name, name) == 0) + if (strcmp(shdict->name, name) == 0) { + found = 1; break; + } } - if (!shdict) { + if (!found) { shdict = kmalloc(sizeof(struct lua_module_shdict), GFP_NOFS); if (shdict == NULL) goto err_unlock; @@ -386,6 +390,16 @@ static int lvm_writer(lua_State *L, const void *b, size_t size, void *B) return 0; } +static void lua_module_free(struct lua_module *module) +{ + kfree(module->chunk); + kfree(module->name); + kfree(module->author); + kfree(module->description); + kfree(module->license); + kfree(module); +} + int lua_module_register(const char *code, size_t len) { struct lua_module *module, *m; @@ -393,6 +407,7 @@ int lua_module_register(const char *code, size_t len) luaL_Buffer B; const char *chunk; size_t chunk_len; + int found = 0; int i; int status; int err; @@ -529,23 +544,25 @@ int lua_module_register(const char *code, size_t len) INIT_LIST_HEAD(&module->kvnodes); spin_lock_init(&module->kvnodes_lock); - write_lock_bh(&modules_lock); + spin_lock(&modules_lock); list_for_each_entry(m, &lsm_modules, list) { - if (strcmp(module->name, m->name) == 0) + if (strcmp(module->name, m->name) == 0) { + found = 1; break; + } } - if (m == NULL) { - list_add_tail(&module->list, &lsm_modules); + if (!found) { + list_add_tail_rcu(&module->list, &lsm_modules); for (i = 0; lua_lsm_hook_stats[i].name; i++) { if (__BITMAP_ISSET(i, &module->hookfuncs)) atomic_inc(&lua_lsm_hook_stats[i].nhooks); } } - write_unlock_bh(&modules_lock); + spin_unlock(&modules_lock); err = -EEXIST; - if (m != NULL) { + if (found) { __log_err("module <%s> registered already\n", module->name); goto err_free_module; } @@ -557,19 +574,14 @@ int lua_module_register(const char *code, size_t len) return 0; err_free_module: - kfree(module->chunk); - kfree(module->name); - kfree(module->author); - kfree(module->description); - kfree(module->license); - kfree(module); + lua_module_free(module); err_free_lua: lua_state_free(L); return err; } -static int lua_module_remove(lua_State *L, struct lua_module *module) +static int lua_module_remove_lvm(lua_State *L, struct lua_module *module) { int removed = 0; @@ -587,34 +599,39 @@ static int lua_module_remove(lua_State *L, struct lua_module *module) int lua_module_unregister(const char *name) { - struct lua_module *module, *tmp; - struct lua_module_shdict *shdict, *shdict_tmp; + struct lua_module *module; + struct lua_module_shdict *shdict, *tmp; struct task_struct *g, *task; unsigned int cpu; + int found = 0; int count = 0; int i; - write_lock_bh(&modules_lock); - list_for_each_entry_safe(module, tmp, &lsm_modules, list) { + spin_lock(&modules_lock); + list_for_each_entry(module, &lsm_modules, list) { if (strcmp(module->name, name) == 0) { - list_del(&module->list); + found = 1; break; } } + if (found) { + list_del_rcu(&module->list); - if (!module) { - write_unlock_bh(&modules_lock); - return -ENOENT; + for (i = 0; lua_lsm_hook_stats[i].name; i++) { + if (__BITMAP_ISSET(i, &module->hookfuncs)) + atomic_dec(&lua_lsm_hook_stats[i].nhooks); + } } + spin_unlock(&modules_lock); + + if (!found) + return -ENOENT; pr_info("Prepare to unregister module <%s> ...\n", name); - for (i = 0; lua_lsm_hook_stats[i].name; i++) { - if (__BITMAP_ISSET(i, &module->hookfuncs)) - atomic_dec(&lua_lsm_hook_stats[i].nhooks); - } + synchronize_srcu(&modules_ss); - list_for_each_entry_safe(shdict, shdict_tmp, &module->shdicts, list) { + list_for_each_entry_safe(shdict, tmp, &module->shdicts, list) { list_del(&shdict->list); kvcache_dict_free(&shdict->dict); kfree(shdict->name); @@ -627,7 +644,7 @@ int lua_module_unregister(const char *name) read_lock(&tasklist_lock); for_each_process_thread(g, task) { lua_State *L = lua_lsm_task(task)->L; - if (lua_module_remove(L, module)) + if (lua_module_remove_lvm(L, module)) count++; } read_unlock(&tasklist_lock); @@ -636,22 +653,16 @@ int lua_module_unregister(const char *name) cpus_read_lock(); for_each_possible_cpu(cpu) { task = idle_task(cpu); - if (lua_module_remove(lua_lsm_task(task)->L, module)) + if (lua_module_remove_lvm(lua_lsm_task(task)->L, module)) count++; } cpus_read_unlock(); - write_unlock_bh(&modules_lock); pr_info("Unregistered module <%s> from %d/%d Lua VMs.\n", name, count, atomic_read(&vm_nalloc) - atomic_read(&vm_nfree)); - kfree(module->chunk); - kfree(module->name); - kfree(module->author); - kfree(module->description); - kfree(module->license); - kfree(module); + lua_module_free(module); return 0; } @@ -660,19 +671,21 @@ int lua_module_unregister(const char *name) int modules_show(struct seq_file *m, void *v) { struct lua_module *module; + int idx; seq_printf(m, "modules for lua-lsm\n"); seq_printf(m, "%-12s %-10s %6s %6s %-48s\n", "name", "license", "size", "nhooks", "author"); seq_printf(m, "%s\n", TABLINE); - read_lock_bh(&modules_lock); - list_for_each_entry(module, &lsm_modules, list) { + idx = srcu_read_lock(&modules_ss); + list_for_each_entry_srcu(module, &lsm_modules, list, + srcu_read_lock_held(&modules_ss)) { seq_printf(m, "%-12s %-10s %6zu %6d %-48s\n", module->name, module->license, module->chunk_len, module->nhooks, module->author); } - read_unlock_bh(&modules_lock); + srcu_read_unlock(&modules_ss, idx); return 0; } @@ -701,7 +714,7 @@ int lua_lsm_status_show(struct seq_file *m, void *v) total = 0; minimum = INT_MAX; maximum = 0; - write_lock_bh(&modules_lock); + spin_lock(&modules_lock); /* TODO: Still need it? */ read_lock(&tasklist_lock); for_each_process_thread(g, p) { L = lua_lsm_task(p)->L; @@ -723,7 +736,7 @@ int lua_lsm_status_show(struct seq_file *m, void *v) maximum = max(maximum, nbytes); } cpus_read_unlock(); - write_unlock_bh(&modules_lock); + spin_unlock(&modules_lock); avg = inuse ? total / inuse : 0; seq_printf(m, " Mem usage: total %d, min %d, max %d, inuse %d, average %d\n", diff --git a/security/lua/lsm.h b/security/lua/lsm.h index 96a85d0a187f..83302387256c 100644 --- a/security/lua/lsm.h +++ b/security/lua/lsm.h @@ -78,7 +78,7 @@ struct lua_module { }; extern struct list_head lsm_modules; -extern rwlock_t modules_lock; +extern struct srcu_struct modules_ss; #define TABLINE "--------------------------------------------" \ diff --git a/security/lua/lsm_defs.h b/security/lua/lsm_defs.h index c5330646f7d3..464f257d4192 100644 --- a/security/lua/lsm_defs.h +++ b/security/lua/lsm_defs.h @@ -132,7 +132,8 @@ lua_getfenv(L, -1); /* save thread.fenv */ \ lua_getfield(L, LUA_REGISTRYINDEX, "_MODULES"); \ /* stack: [traceback, thread, env, _MODULES] */ \ - list_for_each_entry(module, &lsm_modules, list) { \ + list_for_each_entry_srcu(module, &lsm_modules, list, \ + srcu_read_lock_held(&modules_ss)) { \ if (!__BITMAP_ISSET(__LL_NR_ ## NAME, &module->hookfuncs)) \ continue; \ lua_getfield(L, -1, module->name); \ @@ -158,15 +159,16 @@ rettype lua_lsm_ ## NAME(DECL_ARGS_ ## x \ __MAP(x, __SC_DECL, __VA_ARGS__)) \ { \ + int idx; \ int ret; \ START_STATS(NAME); \ - read_lock_bh(&modules_lock); \ + idx = srcu_read_lock(&modules_ss); \ ret = __prepare_ ## NAME(__MAP(x, __SC_ARGS, __VA_ARGS__)); \ if (ret >= 0) { \ ret = __lua_lsm_ ## NAME(__MAP(x, __SC_ARGS, __VA_ARGS__)); \ __postpone_ ## NAME(__MAP(x, __SC_ARGS, __VA_ARGS__)); \ } \ - read_unlock_bh(&modules_lock); \ + srcu_read_unlock(&modules_ss, idx); \ END_STATS(NAME); \ return (rettype)ret; \ } \ From 669358a4159440cd2ed3b7b06d393f6af3d0f446 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Wed, 17 Sep 2025 17:01:37 +0800 Subject: [PATCH 041/110] lua-lsm: lockess shdict with rcu --- security/lua/lsm.c | 52 ++++++++++++++++++++++++++++------------------ security/lua/lsm.h | 4 ++-- 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/security/lua/lsm.c b/security/lua/lsm.c index bb70cb1821c0..06f2102903f7 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -77,11 +77,11 @@ struct lua_lsm_hook_stat lua_lsm_hook_stats[] = { static int lua_shared_index(lua_State *L) { const char *name = luaL_checkstring(L, 2); - struct lua_module_shdict *shdict; + struct lua_module_shdict *shdict, *shtmp; struct lua_module *module; int found = 0; - __log_info("READ shared table, [%s] %s\n", + __log_info_ratelimited("READ shared table, [%s] %s\n", luaL_typename(L, 2), lua_tostring(L, 2) ?: "(null)"); lua_pushlightuserdata(L, MODULE_KEY); @@ -92,25 +92,44 @@ static int lua_shared_index(lua_State *L) } module = lua_touserdata(L, -1); - write_lock(&module->shdict_lock); - list_for_each_entry(shdict, &module->shdicts, list) { + rcu_read_lock(); + list_for_each_entry_rcu(shdict, &module->shdicts, list) { if (strcmp(shdict->name, name) == 0) { found = 1; break; } } + rcu_read_unlock(); + if (!found) { - shdict = kmalloc(sizeof(struct lua_module_shdict), GFP_NOFS); + size_t l = strlen(name); + shdict = kmalloc(struct_size(shdict, name, l + 1), GFP_NOFS); if (shdict == NULL) - goto err_unlock; - shdict->name = kstrdup(name, GFP_NOFS); - if (shdict->name == NULL) - goto err_free; + return 0; kvcache_dict_init(&shdict->dict); - atomic_inc(&module->shdict_count); - list_add_tail(&shdict->list, &module->shdicts); + memcpy(shdict->name, name, l); + shdict->name[l] = '\0'; + + spin_lock(&module->shdict_lock); + list_for_each_entry(shtmp, &module->shdicts, list) { + if (strcmp(shtmp->name, name) == 0) { + found = 1; + break; + } + } + if (!found) { + atomic_inc(&module->shdict_count); + list_add_tail_rcu(&shdict->list, &module->shdicts); + } + spin_unlock(&module->shdict_lock); + + if (found) { + kvcache_dict_free(&shdict->dict); + kfree(shdict); + + shdict = shtmp; + } } - write_unlock(&module->shdict_lock); /* shared[name] = shdict */ lua_pushvalue(L, 2); @@ -120,12 +139,6 @@ static int lua_shared_index(lua_State *L) lua_settop(L, 2); lua_rawget(L, 1); return 1; - -err_free: - kfree(shdict); -err_unlock: - write_unlock(&module->shdict_lock); - return 0; } static int lua_shared_newindex(lua_State *L) @@ -538,7 +551,7 @@ int lua_module_register(const char *code, size_t len) module->chunk_len = chunk_len; INIT_LIST_HEAD(&module->shdicts); - rwlock_init(&module->shdict_lock); + spin_lock_init(&module->shdict_lock); atomic_set(&module->shdict_count, 0); INIT_LIST_HEAD(&module->kvnodes); @@ -634,7 +647,6 @@ int lua_module_unregister(const char *name) list_for_each_entry_safe(shdict, tmp, &module->shdicts, list) { list_del(&shdict->list); kvcache_dict_free(&shdict->dict); - kfree(shdict->name); kfree(shdict); } diff --git a/security/lua/lsm.h b/security/lua/lsm.h index 83302387256c..8be48ef47147 100644 --- a/security/lua/lsm.h +++ b/security/lua/lsm.h @@ -55,8 +55,8 @@ enum { struct lua_module_shdict { struct list_head list; - const char *name; struct kvcache_dict dict; + char name[]; }; struct lua_module { @@ -71,7 +71,7 @@ struct lua_module { char *chunk; size_t chunk_len; struct list_head shdicts; - rwlock_t shdict_lock; + spinlock_t shdict_lock; atomic_t shdict_count; struct list_head kvnodes; spinlock_t kvnodes_lock; From f4a6d8ed31f36b7f28fdb6f4c7c0ab140c8be9ad Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Wed, 17 Sep 2025 20:31:49 +0800 Subject: [PATCH 042/110] lua-lsm: kvcache - Eliminating Inconsistent Locks shdict operations may occur in task context, softirq context (for example, when releasing an object in RCU, the shared dictionary needs to be cleaned up), or worker context. Disabling interrupts during shdict operations ensures that the process or worker contexts are not interrupted, potentially triggering softirq execution and causing lock state errors or deadlocks. Although the situation described above may occur, it involves locks on different shared dictionaries and should not actually cause issues (this remains to be verified). Therefore, the purpose of this document is to eliminate the system warning message, which may be a false alarm. There is a potential deadlock when releasing object kvcache. The following is the log information: [ 0.304479] ================================ [ 0.304648] WARNING: inconsistent lock state [ 0.304770] 6.17.0-rc5+ #570 Not tainted [ 0.304910] -------------------------------- [ 0.305064] inconsistent {SOFTIRQ-ON-W} -> {IN-SOFTIRQ-W} usage. [ 0.305219] swapper/0/0 [HC0[0]:SC1[1]:HE1:SE0] takes: [ 0.305390] ffff0000c3b06880 (&dict->lock){+.?.}-{3:3}, at: kvcache_dict_free+0x24/0x138 [ 0.305843] {SOFTIRQ-ON-W} state was registered at: [ 0.306139] lock_acquire+0x128/0x2cc [ 0.306368] _raw_write_lock+0x4c/0x88 [ 0.306614] kvcache_dict_free+0x24/0x138 [ 0.306873] lua_lsm_file_free_security+0x210/0x264 [ 0.307155] security_file_free+0x54/0x94 [ 0.307403] __fput+0x1b8/0x2fc [ 0.307667] delayed_fput+0x44/0x58 [ 0.307819] process_one_work+0x214/0x564 [ 0.308019] worker_thread+0x244/0x38c [ 0.308239] kthread+0x148/0x26c [ 0.308451] ret_from_fork+0x10/0x20 [ 0.308601] irq event stamp: 12248 [ 0.308746] hardirqs last enabled at (12248): [] __tlb_remove_table+0x7c/0x114 [ 0.309128] hardirqs last disabled at (12247): [] __tlb_remove_table+0x60/0x114 [ 0.309472] softirqs last enabled at (12190): [] handle_softirqs+0x3e0/0x42c [ 0.309802] softirqs last disabled at (12197): [] __do_softirq+0x14/0x20 [ 0.310116] [ 0.310116] other info that might help us debug this: [ 0.310382] Possible unsafe locking scenario: [ 0.310382] [ 0.310599] CPU0 [ 0.310694] ---- [ 0.310790] lock(&dict->lock); [ 0.310911] [ 0.311011] lock(&dict->lock); [ 0.311133] [ 0.311133] *** DEADLOCK *** [ 0.311133] [ 0.311376] 2 locks held by swapper/0/0: [ 0.311526] #0: ffff800081eeefe8 (rcu_callback){....}-{0:0}, at: rcu_lock_acquire+0x0/0x3c [ 0.311915] #1: ffff800081fdb4d0 (modules_ss){.+.?}-{0:0}, at: srcu_lock_acquire+0x0/0x44 [ 0.312265] [ 0.312265] stack backtrace: [ 0.312491] CPU: 0 UID: 0 PID: 0 Comm: swapper/0 Not tainted 6.17.0-rc5+ #570 PREEMPT [ 0.312825] Hardware name: QEMU QEMU Virtual Machine, BIOS edk2-stable202408-prebuilt.qemu.org 08/13/2024 [ 0.313211] Call trace: [ 0.313313] show_stack+0x18/0x24 (C) [ 0.313460] __dump_stack+0x28/0x38 [ 0.313595] dump_stack_lvl+0xcc/0x130 [ 0.313742] dump_stack+0x18/0x44 [ 0.313876] print_usage_bug+0x2f4/0x32c [ 0.314017] mark_lock_irq+0x470/0x47c [ 0.314157] mark_lock+0x1a4/0x21c [ 0.314309] __lock_acquire+0x5f8/0x2cec [ 0.314463] lock_acquire+0x128/0x2cc [ 0.314607] _raw_write_lock+0x4c/0x88 [ 0.314771] kvcache_dict_free+0x24/0x138 [ 0.314932] lua_lsm_inode_free_security_rcu+0x1d8/0x22c [ 0.315158] inode_free_by_rcu+0x50/0x90 [ 0.315319] rcu_core+0x4b4/0xbc0 [ 0.315460] rcu_core_si+0x10/0x1c [ 0.315600] handle_softirqs+0x148/0x42c [ 0.315765] __do_softirq+0x14/0x20 [ 0.315910] ____do_softirq+0x10/0x1c [ 0.316064] call_on_irq_stack+0x30/0x48 [ 0.316230] do_softirq_own_stack+0x1c/0x28 [ 0.316506] __irq_exit_rcu+0xd4/0x18c [ 0.316665] irq_exit_rcu+0x10/0x34 [ 0.316809] el1_interrupt+0x48/0x90 [ 0.316962] el1h_64_irq_handler+0x18/0x24 [ 0.317128] el1h_64_irq+0x80/0x84 [ 0.317269] do_idle+0x200/0x2ec (P) [ 0.317417] cpu_startup_entry+0x34/0x38 [ 0.317579] kernel_init+0x0/0x128 [ 0.317717] start_kernel+0x324/0x444 [ 0.317875] __primary_switched+0x88/0x90 --- security/lua/kvcache.c | 51 +++++++++++++++++++++++++++--------------- security/lua/lsm.c | 5 +++-- 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/security/lua/kvcache.c b/security/lua/kvcache.c index 82f5cad5c4a2..0a5880a0dac0 100644 --- a/security/lua/kvcache.c +++ b/security/lua/kvcache.c @@ -114,12 +114,13 @@ kvcache_lookup(struct kvcache_dict *dict, struct lua_module *module, const char *key) { struct kvcache_node tmp, *node; + unsigned long flags; tmp.key = key; tmp.module = module; - read_lock(&dict->lock); + read_lock_irqsave(&dict->lock, flags); node = RB_FIND(kvcache, &dict->root, &tmp); kvcache_node_hold(node); - read_unlock(&dict->lock); + read_unlock_irqrestore(&dict->lock, flags); return node; } @@ -128,8 +129,9 @@ kvcache_module_link(struct kvcache_dict *dict, struct lua_module *module, struct kvcache_node *node) { struct kvcache_node *prev; + unsigned long flags; - write_lock(&dict->lock); + write_lock_irqsave(&dict->lock, flags); if (atomic_read(&dict->count) >= dict->capacity) { prev = ERR_PTR(-ERANGE); @@ -149,7 +151,7 @@ kvcache_module_link(struct kvcache_dict *dict, } unlock: - write_unlock(&dict->lock); + write_unlock_irqrestore(&dict->lock, flags); return prev; } @@ -170,9 +172,10 @@ static void kvcache_module_unlink(struct kvcache_dict *dict, struct lua_module *module, struct kvcache_node *node) { - write_lock(&dict->lock); + unsigned long flags; + write_lock_irqsave(&dict->lock, flags); kvcache_module_unlink_unlocked(dict, module, node); - write_unlock(&dict->lock); + write_unlock_irqrestore(&dict->lock, flags); } static int kvcache_node_fill(lua_State *L, int idx, struct kvcache_node *node) @@ -217,16 +220,17 @@ kvcache_node_copy(struct kvcache_node *node, struct kvcache_node *src) static int kvcache_node_refill(lua_State *L, int idx, struct kvcache_node *node) { struct kvcache_node ntmp; + unsigned long flags; int err; err = kvcache_node_fill(L, idx, &ntmp); if (err) return err; - write_lock(&node->lock); + write_lock_irqsave(&node->lock, flags); kvcache_node_clear(node); kvcache_node_copy(node, &ntmp); - write_unlock(&node->lock); + write_unlock_irqrestore(&node->lock, flags); return 0; } @@ -290,10 +294,11 @@ kvcache_set(lua_State *L, struct kvcache_dict *dict, struct lua_module *module) static int kvcache_node_get(lua_State *L, struct kvcache_node *node) { struct kvcache_node ntmp; + unsigned long flags; - read_lock(&node->lock); + read_lock_irqsave(&node->lock, flags); kvcache_node_copy(&ntmp, node); - read_unlock(&node->lock); + read_unlock_irqrestore(&node->lock, flags); switch (ntmp.tt) { case LUA_TBOOLEAN: @@ -339,14 +344,15 @@ kvcache_incr(lua_State *L, struct kvcache_dict *dict, struct lua_module *module) node = kvcache_lookup(dict, module, key); if (node) { - write_lock(&node->lock); + unsigned long flags; + write_lock_irqsave(&node->lock, flags); if (node->tt == LUA_TNUMBER) { node->n += n; n = node->n; } else { err = -EINVAL; } - write_unlock(&node->lock); + write_unlock_irqrestore(&node->lock, flags); kvcache_node_drop(node); goto ret; } @@ -406,14 +412,22 @@ void kvcache_module_nodes_gc(struct lua_module *module) void kvcache_dict_free(struct kvcache_dict *dict) { struct kvcache_node *node, *n; - - write_lock(&dict->lock); + unsigned long flags; + + /* + * Avoid being called in softirq, such as the LSM function + * inode_free_security_rcu will run in the RCU softirq context, + * socket_sock_rcv_skb maybe run in the NET_RX softirq to + * receive data, and file_free_security maybe run in the worker + * thread to delay release the file object via delayed_fput. + */ + write_lock_irqsave(&dict->lock, flags); RB_FOREACH_SAFE(node, kvcache, &dict->root, n) { kvcache_module_unlink_unlocked(dict, node->module, node); kvcache_node_drop(node); } WARN_ON(atomic_read(&dict->count) != 0); - write_unlock(&dict->lock); + write_unlock_irqrestore(&dict->lock, flags); } void kvcache_dict_init(struct kvcache_dict *dict) @@ -568,16 +582,17 @@ static int shdict_index(lua_State *L) static int shdict_tostring(lua_State *L) { struct kvcache_dict *shdict = toshdict(L, 1); - read_lock(&shdict->lock); + unsigned long flags; + read_lock_irqsave(&shdict->lock, flags); lua_pushfstring(L, "shdict (%d / %d)", atomic_read(&shdict->count), shdict->capacity); - read_unlock(&shdict->lock); + read_unlock_irqrestore(&shdict->lock, flags); return 1; } static int shdict_gc(lua_State *L) { - __log_info("shdict already freed by unregister\n"); + __log_info_ratelimited("shdict already freed by unregister\n"); return 0; } diff --git a/security/lua/lsm.c b/security/lua/lsm.c index 06f2102903f7..b1f323ad2b2b 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -102,6 +102,7 @@ static int lua_shared_index(lua_State *L) rcu_read_unlock(); if (!found) { + unsigned long flags; size_t l = strlen(name); shdict = kmalloc(struct_size(shdict, name, l + 1), GFP_NOFS); if (shdict == NULL) @@ -110,7 +111,7 @@ static int lua_shared_index(lua_State *L) memcpy(shdict->name, name, l); shdict->name[l] = '\0'; - spin_lock(&module->shdict_lock); + spin_lock_irqsave(&module->shdict_lock, flags); list_for_each_entry(shtmp, &module->shdicts, list) { if (strcmp(shtmp->name, name) == 0) { found = 1; @@ -121,7 +122,7 @@ static int lua_shared_index(lua_State *L) atomic_inc(&module->shdict_count); list_add_tail_rcu(&shdict->list, &module->shdicts); } - spin_unlock(&module->shdict_lock); + spin_unlock_irqrestore(&module->shdict_lock, flags); if (found) { kvcache_dict_free(&shdict->dict); From e2b1efc9f910b13479cf6cdc2d5fd152890c130f Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Thu, 18 Sep 2025 20:35:46 +0800 Subject: [PATCH 043/110] lua-lsm: always show traceback --- security/lua/auxlib.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/security/lua/auxlib.c b/security/lua/auxlib.c index 6e56f8f94408..10a8319a566f 100644 --- a/security/lua/auxlib.c +++ b/security/lua/auxlib.c @@ -61,10 +61,10 @@ void lua_table_dump(lua_State *L, const char *prefix) /* borrowed from lua.c */ int lua_traceback(lua_State *L) { - __log_err("@_@ LuaVM: top = %d\n", lua_gettop(L)); + pr_err("@_@ LuaVM: top = %d\n", lua_gettop(L)); if (!lua_isstring(L, 1)) /* 'message' not a string? */ return 1; /* keep it intact */ - __log_err("@_@ LuaVM: %s\n", lua_tostring(L, -1)); + pr_err("@_@ LuaVM: %s\n", lua_tostring(L, -1)); lua_getfield(L, LUA_GLOBALSINDEX, "debug"); if (!lua_istable(L, -1)) { lua_pop(L, 1); @@ -78,7 +78,7 @@ int lua_traceback(lua_State *L) lua_pushvalue(L, 1); /* pass error message */ lua_pushinteger(L, 2); /* skip this function and traceback */ lua_call(L, 2, 1); /* call debug.traceback */ - __log_err("@_@ LuaVM stack: %s\n", lua_tostring(L, -1)); + pr_err("@_@ LuaVM stack:\n%s\n", lua_tostring(L, -1)); __log_err("------------------ C stack dump start ------------------\n"); dump_stack(); From 24d94a56e99fc9827aa9e00bf3fd74396652b1ab Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Thu, 18 Sep 2025 20:38:02 +0800 Subject: [PATCH 044/110] lua-lsm: use separate Lua VM for softirq Also allow allocate atomic memory. --- security/lua/kvcache.c | 3 +- security/lua/lsm.c | 71 ++++++++++++++++++++++++++++++++++------- security/lua/lsm.h | 14 ++++++++ security/lua/lsm_defs.h | 5 ++- 4 files changed, 79 insertions(+), 14 deletions(-) diff --git a/security/lua/kvcache.c b/security/lua/kvcache.c index 0a5880a0dac0..11f2939776da 100644 --- a/security/lua/kvcache.c +++ b/security/lua/kvcache.c @@ -55,9 +55,8 @@ kvcache_node_alloc(struct kvcache_dict *dict, struct lua_module *module, size_t l = sizeof(struct kvcache_node); struct kvcache_node *node; - WARN_ON(in_atomic()); l += key ? (len + 1) : 0; - node = kmalloc(l, GFP_NOFS); + node = kmalloc(l, lua_lsm_gfp()); if (node == NULL) return NULL; diff --git a/security/lua/lsm.c b/security/lua/lsm.c index b1f323ad2b2b..979771793ea1 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -74,6 +74,37 @@ struct lua_lsm_hook_stat lua_lsm_hook_stats[] = { /********************************** Lua VM **********************************/ +struct lvm_userdata { + bool softirq; +}; + +static struct lvm_userdata irq_lvms_ud = { + .softirq = true, +}; + +static DEFINE_PER_CPU(lua_State *, irq_lvms); + +lua_State *lvm_get(void) +{ + BUG_ON(in_nmi() || in_hardirq()); + + if (in_task()) + return lua_lsm_task(current)->L; + else + return get_cpu_var(irq_lvms); +} + +void lvm_put(lua_State *L) +{ + (void)L; + + if (in_task()) + /* Nothing */; + else + put_cpu_var(irq_lvms); +} + + static int lua_shared_index(lua_State *L) { const char *name = luaL_checkstring(L, 2); @@ -104,9 +135,12 @@ static int lua_shared_index(lua_State *L) if (!found) { unsigned long flags; size_t l = strlen(name); - shdict = kmalloc(struct_size(shdict, name, l + 1), GFP_NOFS); - if (shdict == NULL) + shdict = kmalloc(struct_size(shdict, name, l + 1), + lua_lsm_gfp()); + if (shdict == NULL) { + __log_err("No memory\n"); return 0; + } kvcache_dict_init(&shdict->dict); memcpy(shdict->name, name, l); shdict->name[l] = '\0'; @@ -294,8 +328,10 @@ static atomic_t mem_nfree = ATOMIC_INIT(0); static void *lvm_alloc(void *ud, void *ptr, size_t osize, size_t nsize) { - (void)ud; + struct lvm_userdata *args = ud; + (void)osize; + (void)args; if (nsize == 0) { atomic_inc(&mem_nfree); @@ -306,7 +342,8 @@ static void *lvm_alloc(void *ud, void *ptr, size_t osize, size_t nsize) atomic_inc(&mem_nrealloc); else atomic_inc(&mem_nalloc); - return krealloc(ptr, nsize, GFP_ATOMIC); + + return krealloc(ptr, nsize, lua_lsm_gfp()); } } @@ -351,12 +388,12 @@ static int lvm_pmain(lua_State *L) return 1; } -static lua_State *lua_state_alloc(void) +static lua_State *lua_state_alloc(struct lvm_userdata *args) { lua_State *L; int status; - L = lua_newstate(lvm_alloc, NULL); + L = lua_newstate(lvm_alloc, args); if (L == NULL) return ERR_PTR(-ENOMEM); @@ -426,7 +463,7 @@ int lua_module_register(const char *code, size_t len) int status; int err; - L = lua_state_alloc(); + L = lua_state_alloc(NULL); if (IS_ERR(L)) return PTR_ERR(L); @@ -464,7 +501,7 @@ int lua_module_register(const char *code, size_t len) __log_info("lua module loaded, top = %d\n", lua_gettop(L)); err = -ENOMEM; - module = kzalloc(sizeof(*module), GFP_ATOMIC); + module = kzalloc(sizeof(*module), GFP_KERNEL); if (module == NULL) goto err_free_lua; @@ -545,7 +582,7 @@ int lua_module_register(const char *code, size_t len) if (module->name == NULL) goto err_free_module; - module->chunk = kmalloc(chunk_len, GFP_ATOMIC); + module->chunk = kmalloc(chunk_len, GFP_KERNEL); if (module->chunk == NULL) goto err_free_module; memcpy(module->chunk, chunk, chunk_len); @@ -657,6 +694,7 @@ int lua_module_unregister(const char *name) read_lock(&tasklist_lock); for_each_process_thread(g, task) { lua_State *L = lua_lsm_task(task)->L; + /* TODO: may sleep in rwlock critical section */ if (lua_module_remove_lvm(L, module)) count++; } @@ -668,10 +706,12 @@ int lua_module_unregister(const char *name) task = idle_task(cpu); if (lua_module_remove_lvm(lua_lsm_task(task)->L, module)) count++; + + if (lua_module_remove_lvm(per_cpu(irq_lvms, cpu), module)) + count++; } cpus_read_unlock(); - pr_info("Unregistered module <%s> from %d/%d Lua VMs.\n", name, count, atomic_read(&vm_nalloc) - atomic_read(&vm_nfree)); @@ -795,7 +835,7 @@ int lua_task_blob_init(struct task_struct *task) kvcache_dict_init(&llt->dict); - L = lua_state_alloc(); + L = lua_state_alloc(NULL); if (IS_ERR(L)) return PTR_ERR(L); @@ -847,12 +887,21 @@ static const struct lsm_id lua_lsmid = { static int __init lua_lsm_init(void) { + lua_State *L; + int cpu; int err; err = lua_task_blob_init(current); if (err) return err; + for_each_possible_cpu(cpu) { + L = lua_state_alloc(&irq_lvms_ud); + if (IS_ERR(L)) + return PTR_ERR(L); + per_cpu(irq_lvms, cpu) = L; + } + security_add_hooks(lua_lsm_hooks, ARRAY_SIZE(lua_lsm_hooks), &lua_lsmid); /* Report that Lua-LSM successfully initialized */ diff --git a/security/lua/lsm.h b/security/lua/lsm.h index 8be48ef47147..f1effe80c1d1 100644 --- a/security/lua/lsm.h +++ b/security/lua/lsm.h @@ -84,6 +84,9 @@ extern struct srcu_struct modules_ss; #define TABLINE "--------------------------------------------" \ "--------------------------------------------" +lua_State *lvm_get(void); +void lvm_put(lua_State *L); + int lua_module_register(const char *code, size_t len); int lua_module_unregister(const char *name); @@ -94,6 +97,17 @@ int lua_lsm_status_show(struct seq_file *m, void *v); int lsmhook_stat_show(struct seq_file *m, void *v); #endif +static inline gfp_t lua_lsm_gfp(void) +{ + /* + * Sleeping function is not allowed in atomic, rcu and + * softirq context. + */ + if (in_atomic() || rcu_preempt_depth() > 0) + return GFP_ATOMIC; + else + return GFP_NOFS; +} extern struct lsm_blob_sizes lua_lsm_blob_sizes; diff --git a/security/lua/lsm_defs.h b/security/lua/lsm_defs.h index 464f257d4192..54cc6fde22ab 100644 --- a/security/lua/lsm_defs.h +++ b/security/lua/lsm_defs.h @@ -122,11 +122,12 @@ static inline int __lua_lsm_ ## NAME(DECL_ARGS_ ## x \ __MAP(x, __SC_DECL, __VA_ARGS__)) \ { \ - lua_State *L = lua_lsm_task(current)->L; \ struct lua_module *module; \ + lua_State *L; \ int ret = LSM_RET_DEFAULT(NAME); \ if (atomic_read(&lua_lsm_hook_stats[__LL_NR_ ## NAME].nhooks) == 0) \ return ret; \ + L = lvm_get(); \ lua_pushcfunction(L, lua_traceback); \ lua_pushthread(L); \ lua_getfenv(L, -1); /* save thread.fenv */ \ @@ -138,6 +139,7 @@ continue; \ lua_getfield(L, -1, module->name); \ lua_getfield(L, -1, #NAME); \ + WARN_ON(lua_gettop(L) != 6); \ /* stack: [traceback, thread, env, _MODULES, _M, lfunc] */ \ if (lua_isfunction(L, -1)) { \ int top = lua_gettop(L); \ @@ -154,6 +156,7 @@ RET_CHECK_ ## rettype(NAME, ret); \ } \ lua_pop(L, 4); \ + lvm_put(L); \ return ret; \ } \ rettype lua_lsm_ ## NAME(DECL_ARGS_ ## x \ From 31b296c5b06c2a85750951ca3ad74bd5483ff6a0 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Tue, 14 Oct 2025 10:18:33 +0800 Subject: [PATCH 045/110] lua-lsm: remove rcu api --- security/lua/lua_kernel.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/security/lua/lua_kernel.c b/security/lua/lua_kernel.c index 42acba8401ae..c2c171272e50 100644 --- a/security/lua/lua_kernel.c +++ b/security/lua/lua_kernel.c @@ -313,18 +313,6 @@ static int kernel_ktime_seconds(lua_State *L) return 1; } -static int kernel_rcu_read_lock(lua_State *L) -{ - rcu_read_lock(); - return 0; -} - -static int kernel_rcu_read_unlock(lua_State *L) -{ - rcu_read_unlock(); - return 0; -} - static int kernel_printk(lua_State *L) { const char *s = luaL_checkstring(L, 1); @@ -361,8 +349,6 @@ static const luaL_Reg kernellib[] = { { "lsm_funcs", kernel_lsm_funcs }, { "random", kernel_random }, { "ktime_seconds", kernel_ktime_seconds }, - { "rcu_read_lock", kernel_rcu_read_lock }, - { "rcu_read_unlock", kernel_rcu_read_unlock }, { "printk", kernel_printk }, #define XX(name) { "pr_" #name, kernel_pr_ ## name }, From d90698696fb2678a1a6007849a2b79eb47b269f1 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Tue, 14 Oct 2025 19:45:21 +0800 Subject: [PATCH 046/110] lua-lsm: Add missing updates for exist node --- security/lua/kvcache.c | 148 +++++++++++++++++++---------------------- 1 file changed, 70 insertions(+), 78 deletions(-) diff --git a/security/lua/kvcache.c b/security/lua/kvcache.c index 11f2939776da..d0c352d6d840 100644 --- a/security/lua/kvcache.c +++ b/security/lua/kvcache.c @@ -29,19 +29,17 @@ static int kvcache_node_cmp(struct kvcache_node *n1, struct kvcache_node *n2) RB_GENERATE_STATIC(kvcache, kvcache_node, node, kvcache_node_cmp); -static int kvcache_result(lua_State *L, int err, int nresults) +static int kvcache_result(lua_State *L, int err) { - const char *error = "unknown error"; - - if (err >= 0) - return nresults; + const char *error; switch (err) { - case -ENOMEM: error = "no memory"; break; - case -EINVAL: error = "invalid value"; break; - case -ERANGE: error = "no space"; break; - case -ESRCH: error = "no module"; break; - case -EEXIST: error = "exists"; break; + case -ENOMEM: error = "no memory"; break; + case -EINVAL: error = "invalid"; break; + case -ERANGE: error = "no space"; break; + case -ESRCH: error = "no module"; break; + case -EEXIST: error = "exists"; break; + default: error = "unknown"; break; } lua_pushnil(L); lua_pushstring(L, error); @@ -241,53 +239,50 @@ kvcache_set(lua_State *L, struct kvcache_dict *dict, struct lua_module *module) const char *key = luaL_checklstring(L, 2, &len); int tt = lua_type(L, 3); struct kvcache_node *node, *prev; - int err = 0; + int err; node = kvcache_lookup(dict, module, key); - if (node) { + if (node == NULL) { + if (tt == LUA_TNIL) + goto ret; + + node = kvcache_node_alloc(dict, module, key, len); + if (node == NULL) + return kvcache_result(L, -ENOMEM); + + err = kvcache_node_fill(L, 3, node); + if (err) { + kvcache_node_free(node); + return kvcache_result(L, err); + } + + prev = kvcache_module_link(dict, module, node); + if (prev) { + kvcache_node_free(node); + + if (IS_ERR(prev)) + return kvcache_result(L, PTR_ERR(prev)); + + err = kvcache_node_refill(L, 3, prev); + kvcache_node_drop(prev); + } + } else { if (tt == LUA_TNIL) { kvcache_module_unlink(dict, module, node); kvcache_node_drop(node); + err = 0; } else { err = kvcache_node_refill(L, 3, node); } kvcache_node_drop(node); - goto ret; - } - - if (tt == LUA_TNIL) - goto ret; - - err = -ENOMEM; - node = kvcache_node_alloc(dict, module, key, len); - if (node == NULL) - goto ret; - - err = kvcache_node_fill(L, 3, node); - if (err) { - kvcache_node_free(node); - goto ret; - } - - err = -EEXIST; - prev = kvcache_module_link(dict, module, node); - if (prev) { - if (IS_ERR(prev)) - err = PTR_ERR(prev); - else - kvcache_node_drop(prev); - - kvcache_node_free(node); - goto ret; } - err = 0; + if (err) + return kvcache_result(L, err); ret: - if (!err) - lua_pushboolean(L, 1); - - return kvcache_result(L, err, 1); + lua_pushboolean(L, 1); + return 1; } static int kvcache_node_get(lua_State *L, struct kvcache_node *node) @@ -339,50 +334,46 @@ kvcache_incr(lua_State *L, struct kvcache_dict *dict, struct lua_module *module) const char *key = luaL_checklstring(L, 2, &len); lua_Number n = luaL_optnumber(L, 3, 1); struct kvcache_node *node, *prev; - int err = 0; + unsigned long flags; + int err; node = kvcache_lookup(dict, module, key); - if (node) { - unsigned long flags; + if (node == NULL) { + node = kvcache_node_alloc(dict, module, key, len); + if (node == NULL) + return kvcache_result(L, -ENOMEM); + + node->n = n; + node->tt = LUA_TNUMBER; + + prev = kvcache_module_link(dict, module, node); + if (prev) { + kvcache_node_free(node); + + if (IS_ERR(prev)) + return kvcache_result(L, PTR_ERR(prev)); + + node = prev; + goto update; + } + } else { +update: + err = -EINVAL; write_lock_irqsave(&node->lock, flags); if (node->tt == LUA_TNUMBER) { node->n += n; n = node->n; - } else { - err = -EINVAL; + err = 0; } write_unlock_irqrestore(&node->lock, flags); kvcache_node_drop(node); - goto ret; - } - err = -ENOMEM; - node = kvcache_node_alloc(dict, module, key, len); - if (node == NULL) - goto ret; - - node->n = n; - node->tt = LUA_TNUMBER; - - err = -EEXIST; - prev = kvcache_module_link(dict, module, node); - if (prev) { - if (IS_ERR(prev)) - err = PTR_ERR(prev); - else - kvcache_node_drop(prev); - - kvcache_node_free(node); - goto ret; + if (err) + return kvcache_result(L, err); } - err = 0; - -ret: - if (!err) - lua_pushnumber(L, n); - - return kvcache_result(L, err, 1); + lua_pushnumber(L, n); + return 1; } void kvcache_module_nodes_gc(struct lua_module *module) @@ -492,7 +483,7 @@ int lua_object_incr(lua_State *L, struct kvcache_dict *dict) module = module_from_object_fenv(L, 1); if (module == NULL) - return kvcache_result(L, -ESRCH, 0); + return kvcache_result(L, -ESRCH); return kvcache_incr(L, dict, module); } @@ -537,7 +528,8 @@ int lua_object_newindex(lua_State *L, struct kvcache_dict *dict) module = module_from_object_fenv(L, 1); if (module == NULL) - return kvcache_result(L, -ESRCH, 0); + return kvcache_result(L, -ESRCH); + return kvcache_set(L, dict, module); } From b235c17c4f358fd0de40cc83f5dbaef8025e2387 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Tue, 14 Oct 2025 21:16:26 +0800 Subject: [PATCH 047/110] lua-lsm: Fix mismatched build configuration name --- security/lua/lsm.c | 2 +- security/lua/lsm.h | 4 ++-- security/lua/lsm_defs.h | 2 +- security/lua/securityfs.c | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/security/lua/lsm.c b/security/lua/lsm.c index 979771793ea1..783b422f5656 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -743,7 +743,7 @@ int modules_show(struct seq_file *m, void *v) } -#ifdef CONFIG_SECURITY_LUA_LSM_STATS +#ifdef CONFIG_SECURITY_LUA_LSM_STATISTICS int lua_lsm_status_show(struct seq_file *m, void *v) { diff --git a/security/lua/lsm.h b/security/lua/lsm.h index f1effe80c1d1..ab6c3d97519a 100644 --- a/security/lua/lsm.h +++ b/security/lua/lsm.h @@ -28,7 +28,7 @@ extern int lua_lsm_initialized __initdata; struct lua_lsm_hook_stat { const char *name; atomic_t nhooks; -#ifdef CONFIG_SECURITY_LUA_LSM_STATS +#ifdef CONFIG_SECURITY_LUA_LSM_STATISTICS atomic_t count; atomic64_t time; /* ns */ atomic64_t maxtime; @@ -92,7 +92,7 @@ int lua_module_unregister(const char *name); int modules_show(struct seq_file *m, void *v); -#ifdef CONFIG_SECURITY_LUA_LSM_STATS +#ifdef CONFIG_SECURITY_LUA_LSM_STATISTICS int lua_lsm_status_show(struct seq_file *m, void *v); int lsmhook_stat_show(struct seq_file *m, void *v); #endif diff --git a/security/lua/lsm_defs.h b/security/lua/lsm_defs.h index 54cc6fde22ab..bbad812bcc77 100644 --- a/security/lua/lsm_defs.h +++ b/security/lua/lsm_defs.h @@ -19,7 +19,7 @@ #undef LSM_HOOK -#ifdef CONFIG_SECURITY_LUA_LSM_STATS +#ifdef CONFIG_SECURITY_LUA_LSM_STATISTICS #define START_STATS(NAME) \ do { \ diff --git a/security/lua/securityfs.c b/security/lua/securityfs.c index 22fef3553cc6..cd21e7845220 100644 --- a/security/lua/securityfs.c +++ b/security/lua/securityfs.c @@ -134,7 +134,7 @@ static const struct file_operations fops_module = { .release = single_release, }; -#ifdef CONFIG_SECURITY_LUA_LSM_STATS +#ifdef CONFIG_SECURITY_LUA_LSM_STATISTICS static int open_status(struct inode *inode, struct file *filp) { @@ -172,7 +172,7 @@ static struct lua_file { { "register", 0222, &fops_register }, /* -w--w--w- */ { "unregister", 0222, &fops_unregister }, /* -w--w--w- */ { "module", 0444, &fops_module }, /* r--r--r-- */ -#ifdef CONFIG_SECURITY_LUA_LSM_STATS +#ifdef CONFIG_SECURITY_LUA_LSM_STATISTICS { "status", 0444, &fops_status }, /* r--r--r-- */ { "lsm_funcs", 0444, &fops_lsm_funcs }, /* r--r--r-- */ #endif From edc83367cbb68b0a7cb68b38db815d2ee1a8bb41 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Sun, 19 Oct 2025 15:13:57 +0800 Subject: [PATCH 048/110] lua-lsm: Fix crash caused by incomplete kvnodes free when module is unloaded --- security/lua/kvcache.c | 21 +++++++++++++++++---- security/lua/kvcache.h | 2 +- security/lua/lsm.c | 1 + security/lua/lsm.h | 1 + 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/security/lua/kvcache.c b/security/lua/kvcache.c index d0c352d6d840..d754642b8472 100644 --- a/security/lua/kvcache.c +++ b/security/lua/kvcache.c @@ -143,6 +143,7 @@ kvcache_module_link(struct kvcache_dict *dict, if (module) { spin_lock(&module->kvnodes_lock); list_add_tail(&node->modlist, &module->kvnodes); + atomic_inc(&module->kvnodes_count); spin_unlock(&module->kvnodes_lock); } } @@ -161,6 +162,7 @@ kvcache_module_unlink_unlocked(struct kvcache_dict *dict, if (module) { spin_lock(&module->kvnodes_lock); list_del(&node->modlist); + atomic_dec(&module->kvnodes_count); spin_unlock(&module->kvnodes_lock); } } @@ -376,17 +378,22 @@ kvcache_incr(lua_State *L, struct kvcache_dict *dict, struct lua_module *module) return 1; } -void kvcache_module_nodes_gc(struct lua_module *module) +int kvcache_module_nodes_gc(struct lua_module *module) { struct list_head cleanup_list; struct kvcache_node *node, *tmp; + unsigned long flags; + int count, n = 0; + + count = atomic_read(&module->kvnodes_count); INIT_LIST_HEAD(&cleanup_list); - spin_lock(&module->kvnodes_lock); - list_cut_position(&cleanup_list, &module->kvnodes, module->kvnodes.next); - spin_unlock(&module->kvnodes_lock); + spin_lock_irqsave(&module->kvnodes_lock, flags); + list_cut_position(&cleanup_list, &module->kvnodes, module->kvnodes.prev); + spin_unlock_irqrestore(&module->kvnodes_lock, flags); list_for_each_entry_safe(node, tmp, &cleanup_list, modlist) { + /* TODO: node maybe freed by kvcache_dict_free() */ struct kvcache_dict *dict = node->dict; BUG_ON(!dict); @@ -396,7 +403,13 @@ void kvcache_module_nodes_gc(struct lua_module *module) */ kvcache_module_unlink(dict, NULL, node); kvcache_node_drop(node); + n += 1; } + + __log_info("module <%s>, kvnodes_count = %d, freed = %d\n", + module->name, count, n); + WARN_ON(count != n); + return n; } void kvcache_dict_free(struct kvcache_dict *dict) diff --git a/security/lua/kvcache.h b/security/lua/kvcache.h index a6de1196b081..e29ae4362969 100644 --- a/security/lua/kvcache.h +++ b/security/lua/kvcache.h @@ -42,7 +42,7 @@ struct kvcache_dict { RB_HEAD(kvcache, kvcache_node) root; }; -void kvcache_module_nodes_gc(struct lua_module *module); +int kvcache_module_nodes_gc(struct lua_module *module); void kvcache_dict_free(struct kvcache_dict *dict); void kvcache_dict_init(struct kvcache_dict *dict); void kvcache_status(int *nalloc, int *nfree); diff --git a/security/lua/lsm.c b/security/lua/lsm.c index 783b422f5656..e5f97607ca44 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -594,6 +594,7 @@ int lua_module_register(const char *code, size_t len) INIT_LIST_HEAD(&module->kvnodes); spin_lock_init(&module->kvnodes_lock); + atomic_set(&module->kvnodes_count, 0); spin_lock(&modules_lock); list_for_each_entry(m, &lsm_modules, list) { diff --git a/security/lua/lsm.h b/security/lua/lsm.h index ab6c3d97519a..f8b4e76d5518 100644 --- a/security/lua/lsm.h +++ b/security/lua/lsm.h @@ -75,6 +75,7 @@ struct lua_module { atomic_t shdict_count; struct list_head kvnodes; spinlock_t kvnodes_lock; + atomic_t kvnodes_count; }; extern struct list_head lsm_modules; From 2d6926053bdacfef823ee93217c684839839aabf Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Mon, 20 Oct 2025 15:07:01 +0800 Subject: [PATCH 049/110] lua-lsm: add refcount to per task LuaVM --- security/lua/kvcache.c | 2 +- security/lua/lsm.c | 36 +++++++++++++++++++++++++++++++----- security/lua/lsm.h | 1 + security/lua/refcount.h | 9 ++++----- 4 files changed, 37 insertions(+), 11 deletions(-) diff --git a/security/lua/kvcache.c b/security/lua/kvcache.c index d754642b8472..3ac65665933e 100644 --- a/security/lua/kvcache.c +++ b/security/lua/kvcache.c @@ -102,7 +102,7 @@ static void kvcache_node_drop(struct kvcache_node *node) { if (node == NULL) return; - if (refcount_release(&node->refcount)) + if (refcount_release(&node->refcount) == 0) kvcache_node_free(node); } diff --git a/security/lua/lsm.c b/security/lua/lsm.c index e5f97607ca44..6645ca6d058a 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -84,22 +84,39 @@ static struct lvm_userdata irq_lvms_ud = { static DEFINE_PER_CPU(lua_State *, irq_lvms); +static lua_State *lvm_get_from_task(const struct task_struct *task) +{ + struct lua_lsm_task *llt = lua_lsm_task(task); + int n = refcount_acquire(&llt->refcount); + WARN_ON(n != 1); + KASSERT(n == 1, ("<%s> Lua VM is reused, refcount = %d\n", + task->comm, n)); + return llt->L; +} + +static void lvm_put_to_task(const struct task_struct *task, lua_State *L) +{ + struct lua_lsm_task *llt = lua_lsm_task(task); + int n = refcount_release(&llt->refcount); + KASSERT(n == 0, ("<%s> Lua VM is reused, refcount = %d\n", + task->comm, n)); + (void)L; +} + lua_State *lvm_get(void) { BUG_ON(in_nmi() || in_hardirq()); if (in_task()) - return lua_lsm_task(current)->L; + return lvm_get_from_task(current); else return get_cpu_var(irq_lvms); } void lvm_put(lua_State *L) { - (void)L; - if (in_task()) - /* Nothing */; + lvm_put_to_task(current, L); else put_cpu_var(irq_lvms); } @@ -694,22 +711,30 @@ int lua_module_unregister(const char *name) /* remove loaded module from every Lua VM */ read_lock(&tasklist_lock); for_each_process_thread(g, task) { - lua_State *L = lua_lsm_task(task)->L; + lua_State *L = lvm_get_from_task(task); /* TODO: may sleep in rwlock critical section */ if (lua_module_remove_lvm(L, module)) count++; + lvm_put_to_task(task, L); } read_unlock(&tasklist_lock); /* ditto for the idle 'swapper' tasks */ cpus_read_lock(); for_each_possible_cpu(cpu) { + /* TODO: remove 'swapper' tasks Lua VM */ task = idle_task(cpu); if (lua_module_remove_lvm(lua_lsm_task(task)->L, module)) count++; + /* + * Disable softirq to prevent triggered softirq or RCU from + * changing the Lua VM environment. + */ + local_bh_disable(); if (lua_module_remove_lvm(per_cpu(irq_lvms, cpu), module)) count++; + local_bh_enable(); } cpus_read_unlock(); @@ -841,6 +866,7 @@ int lua_task_blob_init(struct task_struct *task) return PTR_ERR(L); llt->L = L; + refcount_init(&llt->refcount, 0); return 0; } diff --git a/security/lua/lsm.h b/security/lua/lsm.h index f8b4e76d5518..d171d7a3bf45 100644 --- a/security/lua/lsm.h +++ b/security/lua/lsm.h @@ -114,6 +114,7 @@ extern struct lsm_blob_sizes lua_lsm_blob_sizes; struct lua_lsm_task { lua_State *L; + atomic_t refcount; struct kvcache_dict dict; }; diff --git a/security/lua/refcount.h b/security/lua/refcount.h index 805ec526d809..b59fd8e21cba 100644 --- a/security/lua/refcount.h +++ b/security/lua/refcount.h @@ -25,20 +25,19 @@ refcount_init(atomic_t *count, long value) atomic_set(count, value); } -static inline void +static inline int refcount_acquire(atomic_t *count) { KASSERT(atomic_read(count) < UINT_MAX, ("refcount %p overflowed", count)); - atomic_inc(count); + return atomic_inc_return(count); } static inline int refcount_release(atomic_t *count) { - long n; - n = atomic_dec_return(count); + int n = atomic_dec_return(count); KASSERT(n >= 0, ("negative refcount %p", count)); - return (n == 0); + return n; } #endif /* ! _SECURITY_LUA_LSM_REFCOUNT_H */ From e9a93868ec0e1608db4e65a00ced26e27f99ffcb Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Tue, 21 Oct 2025 10:58:27 +0800 Subject: [PATCH 050/110] lua-lsm: Fixes srcu read on modules list --- security/lua/lsm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/security/lua/lsm.c b/security/lua/lsm.c index 6645ca6d058a..d4dc63e75e94 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -284,7 +284,8 @@ static int lua_module_index(lua_State *L) int err; /* module queries are always run with a read lock */ - list_for_each_entry(module, &lsm_modules, list) { + list_for_each_entry_srcu(module, &lsm_modules, list, + srcu_read_lock_held(&modules_ss)) { if (strcmp(module->name, key) != 0) continue; From 1e43476e7ed12ba4b03ef1bd24b5143b0de7b834 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Tue, 21 Oct 2025 10:59:12 +0800 Subject: [PATCH 051/110] lua-lsm: add loaded_count for lua_module --- security/lua/lsm.c | 12 ++++++++++-- security/lua/lsm.h | 1 + 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/security/lua/lsm.c b/security/lua/lsm.c index d4dc63e75e94..1306d134929a 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -300,6 +300,8 @@ static int lua_module_index(lua_State *L) lua_pushvalue(L, -2); /* stack: [table, thunk, key, thunk] */ lua_rawset(L, 1); /* table[key] = thunk */ + + atomic_inc(&module->loaded_count); return 1; /* return the thunk */ } @@ -340,6 +342,7 @@ static int ll_require(lua_State *L) static atomic_t vm_nalloc = ATOMIC_INIT(0); static atomic_t vm_nfree = ATOMIC_INIT(0); +static atomic_t vm_inuse = ATOMIC_INIT(0); static atomic_t mem_nalloc = ATOMIC_INIT(0); static atomic_t mem_nrealloc = ATOMIC_INIT(0); static atomic_t mem_nfree = ATOMIC_INIT(0); @@ -439,6 +442,7 @@ static lua_State *lua_state_alloc(struct lvm_userdata *args) } atomic_inc(&vm_nalloc); + atomic_inc(&vm_inuse); return L; } @@ -446,6 +450,7 @@ static void lua_state_free(lua_State *L) { if (L) { atomic_inc(&vm_nfree); + atomic_dec(&vm_inuse); lua_close(L); } } @@ -720,6 +725,8 @@ int lua_module_unregister(const char *name) } read_unlock(&tasklist_lock); + __log_info("Prepare to unregister module <%s> from swapper\n", name); + /* ditto for the idle 'swapper' tasks */ cpus_read_lock(); for_each_possible_cpu(cpu) { @@ -739,8 +746,9 @@ int lua_module_unregister(const char *name) } cpus_read_unlock(); - pr_info("Unregistered module <%s> from %d/%d Lua VMs.\n", - name, count, atomic_read(&vm_nalloc) - atomic_read(&vm_nfree)); + pr_info("Unregistered module <%s> from %d/%d Lua VMs, freed = %d.\n", + name, atomic_read(&module->loaded_count), + atomic_read(&vm_inuse), count); lua_module_free(module); return 0; diff --git a/security/lua/lsm.h b/security/lua/lsm.h index d171d7a3bf45..8a86d21d5324 100644 --- a/security/lua/lsm.h +++ b/security/lua/lsm.h @@ -70,6 +70,7 @@ struct lua_module { int nhooks; char *chunk; size_t chunk_len; + atomic_t loaded_count; struct list_head shdicts; spinlock_t shdict_lock; atomic_t shdict_count; From a79c1d43a631e3853a2936a8c3136c036db151c3 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Thu, 23 Oct 2025 09:20:15 +0800 Subject: [PATCH 052/110] lua-lsm: safe unregister module under freeze LuaVM Calibrate load count when releasing LuaVM. Add module state for safe unregister. --- security/lua/debug.h | 2 +- security/lua/kvcache.c | 1 + security/lua/lsm.c | 279 ++++++++++++++++++++++++++++++++-------- security/lua/lsm.h | 10 +- security/lua/lsm_defs.h | 2 + 5 files changed, 237 insertions(+), 57 deletions(-) diff --git a/security/lua/debug.h b/security/lua/debug.h index 768d3c18e648..4239e4232891 100644 --- a/security/lua/debug.h +++ b/security/lua/debug.h @@ -20,7 +20,7 @@ extern bool debug; do { \ if (debug) \ pr_ ## level("%s [%d]: %d %s - " fmt, \ - current->comm, task_pid_vnr(current), \ + current->comm, task_pid_nr(current), \ __LINE__, __func__, ##__VA_ARGS__); \ } while (0) diff --git a/security/lua/kvcache.c b/security/lua/kvcache.c index 3ac65665933e..61d56c10a852 100644 --- a/security/lua/kvcache.c +++ b/security/lua/kvcache.c @@ -409,6 +409,7 @@ int kvcache_module_nodes_gc(struct lua_module *module) __log_info("module <%s>, kvnodes_count = %d, freed = %d\n", module->name, count, n); WARN_ON(count != n); + atomic_set(&module->kvnodes_count, count - n); return n; } diff --git a/security/lua/lsm.c b/security/lua/lsm.c index 1306d134929a..f9a5a54da746 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -47,7 +47,7 @@ int lua_lsm_initialized __initdata; /********************************* lsm hook *********************************/ struct list_head lsm_modules = LIST_HEAD_INIT(lsm_modules); -static DEFINE_SPINLOCK(modules_lock); +static DEFINE_MUTEX(modules_mutex); DEFINE_SRCU(modules_ss); struct lua_lsm_hook_stat lua_lsm_hook_stats[] = { @@ -84,10 +84,15 @@ static struct lvm_userdata irq_lvms_ud = { static DEFINE_PER_CPU(lua_State *, irq_lvms); -static lua_State *lvm_get_from_task(const struct task_struct *task) +static lua_State * +lvm_get_from_task(const struct task_struct *task, bool exclusive) { struct lua_lsm_task *llt = lua_lsm_task(task); int n = refcount_acquire(&llt->refcount); + if (exclusive && n != 1) { + refcount_release(&llt->refcount); + return NULL; + } WARN_ON(n != 1); KASSERT(n == 1, ("<%s> Lua VM is reused, refcount = %d\n", task->comm, n)); @@ -100,7 +105,7 @@ static void lvm_put_to_task(const struct task_struct *task, lua_State *L) int n = refcount_release(&llt->refcount); KASSERT(n == 0, ("<%s> Lua VM is reused, refcount = %d\n", task->comm, n)); - (void)L; + WARN_ON(L != llt->L); } lua_State *lvm_get(void) @@ -108,7 +113,7 @@ lua_State *lvm_get(void) BUG_ON(in_nmi() || in_hardirq()); if (in_task()) - return lvm_get_from_task(current); + return lvm_get_from_task(current, false); else return get_cpu_var(irq_lvms); } @@ -301,7 +306,7 @@ static int lua_module_index(lua_State *L) /* stack: [table, thunk, key, thunk] */ lua_rawset(L, 1); /* table[key] = thunk */ - atomic_inc(&module->loaded_count); + atomic_inc(&module->nloaded); return 1; /* return the thunk */ } @@ -309,6 +314,35 @@ static int lua_module_index(lua_State *L) return 0; } +/* + * When LuaVM is destroyed, iterate over the modules loaded in the VM + * and update the load count in the module. + */ +static void lua_modules_free(struct task_struct *task, lua_State *L) +{ + struct lua_module *module; + + lua_getfield(L, LUA_REGISTRYINDEX, "_MODULES"); + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + return; + } + + list_for_each_entry_srcu(module, &lsm_modules, list, + srcu_read_lock_held(&modules_ss)) { + lua_pushstring(L, module->name); + lua_rawget(L, -2); + if (lua_istable(L, -1)) { + atomic_dec(&module->nloaded); + __log_info("<%s>: %d-%d freed module <%s>, nloaded = %d\n", + task->comm, task_tgid_nr(task), task_pid_nr(task), + module->name, atomic_read(&module->nloaded)); + } + lua_pop(L, 1); + } + lua_pop(L, 1); +} + static const luaL_Reg builtinlibs[] = { { "kernel", luaopen_kernel }, @@ -528,6 +562,7 @@ int lua_module_register(const char *code, size_t len) if (module == NULL) goto err_free_lua; + module->state = LMS_STATE_COMING; __BITMAP_ZERO(&module->hookfuncs); /* traversal the result table */ @@ -619,7 +654,7 @@ int lua_module_register(const char *code, size_t len) spin_lock_init(&module->kvnodes_lock); atomic_set(&module->kvnodes_count, 0); - spin_lock(&modules_lock); + mutex_lock(&modules_mutex); list_for_each_entry(m, &lsm_modules, list) { if (strcmp(module->name, m->name) == 0) { found = 1; @@ -627,14 +662,15 @@ int lua_module_register(const char *code, size_t len) } } if (!found) { - list_add_tail_rcu(&module->list, &lsm_modules); - for (i = 0; lua_lsm_hook_stats[i].name; i++) { if (__BITMAP_ISSET(i, &module->hookfuncs)) atomic_inc(&lua_lsm_hook_stats[i].nhooks); } + + module->state = LMS_STATE_LIVE; + list_add_tail_rcu(&module->list, &lsm_modules); } - spin_unlock(&modules_lock); + mutex_unlock(&modules_mutex); err = -EEXIST; if (found) { @@ -656,51 +692,139 @@ int lua_module_register(const char *code, size_t len) return err; } -static int lua_module_remove_lvm(lua_State *L, struct lua_module *module) +static int lvm_remove_module(lua_State *L, struct lua_module *module) { - int removed = 0; - + int err = -ENOENT; /* registry._MODULES[modname] = nil */ lua_getfield(L, LUA_REGISTRYINDEX, "_MODULES"); if (lua_istable(L, -1)) { lua_pushstring(L, module->name); - lua_pushnil(L); - lua_rawset(L, -3); - removed = 1; + lua_rawget(L, -2); + if (lua_istable(L, -1)) { + lua_pop(L, 1); + + lua_pushstring(L, module->name); + lua_pushnil(L); + lua_rawset(L, -3); + err = 0; + } else { + lua_pop(L, 1); + } } lua_pop(L, 1); - return removed; + return err; +} + +static int task_remove_module(struct task_struct *task, void *arg) +{ + struct lua_module *module = arg; + lua_State *L; + int err; + + if (task_curr(task) && task != current) + return -EBUSY; + + L = lvm_get_from_task(task, true); + if (L == NULL) + return -EAGAIN; + + err = lvm_remove_module(L, module); + lvm_put_to_task(task, L); + return err; +} + +static int tasks_lvm_remove_module(struct lua_module *module, int *nbusy) +{ + struct task_struct *g, *task; + int count = 0; + int err; + + *nbusy = 0; + /* remove loaded module from every Lua VM */ + read_lock(&tasklist_lock); + for_each_process_thread(g, task) { + if (task == current) + err = task_remove_module(task, module); + else + err = task_call_func(task, task_remove_module, module); + + if (!err) { + count++; + __log_info("<%s>: err = [ OK ] \t<%s>: %d-%d\n", module->name, + task->comm, task_tgid_nr(task), task_pid_nr(task)); + } else if (err == -EBUSY || err == -EAGAIN) { + *nbusy += 1; + __log_info("<%s>: err = %s \t<%s>: %d-%d\n", + module->name, err == -EBUSY ? "EBUSY" : "EAGAIN", + task->comm, task_tgid_nr(task), task_pid_nr(task)); + } else { + __log_info_ratelimited("<%s>: err = %s \t<%s>: %d-%d\n", + module->name, err == -ENOENT ? "[ENOENT]" : "unknown", + task->comm, task_tgid_nr(task), task_pid_nr(task)); + } + } + read_unlock(&tasklist_lock); + return count; +} + +/* + * Due to the limitations of schedule_on_each_cpu(), global variables + * are used to pass parameters to the callback function. + */ +static struct lua_module *work_ctx_remove_module; +static atomic_t work_ctx_remove_count; + +static void softirq_lvm_remove_module(struct work_struct *work) +{ + struct lua_module *module = work_ctx_remove_module; + int cpu = smp_processor_id(); + int err; + + WARN_ON(work_ctx_remove_module == NULL); + /* + * Disable softirq to prevent triggered softirq or RCU from + * changing the Lua VM environment. + */ + local_bh_disable(); + err = lvm_remove_module(per_cpu(irq_lvms, cpu), module); + if (!err) { + atomic_inc(&work_ctx_remove_count); + __log_info("<%s>: err = [ OK ] \t, count = %d\n", + module->name, cpu, atomic_read(&work_ctx_remove_count)); + } + local_bh_enable(); } int lua_module_unregister(const char *name) { struct lua_module *module; struct lua_module_shdict *shdict, *tmp; - struct task_struct *g, *task; + int count = 0, nloaded, nbusy; unsigned int cpu; int found = 0; - int count = 0; + int err; int i; - spin_lock(&modules_lock); + mutex_lock(&modules_mutex); list_for_each_entry(module, &lsm_modules, list) { if (strcmp(module->name, name) == 0) { found = 1; break; } } - if (found) { - list_del_rcu(&module->list); + if (found && module->state == LMS_STATE_LIVE) { + module->state = LMS_STATE_GOING; for (i = 0; lua_lsm_hook_stats[i].name; i++) { if (__BITMAP_ISSET(i, &module->hookfuncs)) atomic_dec(&lua_lsm_hook_stats[i].nhooks); } } - spin_unlock(&modules_lock); - if (!found) + if (!found) { + mutex_unlock(&modules_mutex); return -ENOENT; + } pr_info("Prepare to unregister module <%s> ...\n", name); @@ -714,44 +838,88 @@ int lua_module_unregister(const char *name) kvcache_module_nodes_gc(module); + nloaded = atomic_read(&module->nloaded); /* remove loaded module from every Lua VM */ - read_lock(&tasklist_lock); - for_each_process_thread(g, task) { - lua_State *L = lvm_get_from_task(task); - /* TODO: may sleep in rwlock critical section */ - if (lua_module_remove_lvm(L, module)) - count++; - lvm_put_to_task(task, L); - } - read_unlock(&tasklist_lock); - - __log_info("Prepare to unregister module <%s> from swapper\n", name); + if (nloaded > 0) { + count += tasks_lvm_remove_module(module, &nbusy); - /* ditto for the idle 'swapper' tasks */ - cpus_read_lock(); - for_each_possible_cpu(cpu) { - /* TODO: remove 'swapper' tasks Lua VM */ - task = idle_task(cpu); - if (lua_module_remove_lvm(lua_lsm_task(task)->L, module)) - count++; + __log_info("Unregister module <%s> from task, freed = %d/%d, nbusy = %d\n", + name, count, nloaded, nbusy); + } + /* + * At this time, LuaVM may still be released asynchronously, + * so the nloaded will be updated in the air. + */ + nloaded = atomic_read(&module->nloaded); + if (count < nloaded) { /* - * Disable softirq to prevent triggered softirq or RCU from - * changing the Lua VM environment. + * Since global variables are used, locking ensures that only + * one instance of the softirq LuaVM offload is executed. */ - local_bh_disable(); - if (lua_module_remove_lvm(per_cpu(irq_lvms, cpu), module)) - count++; - local_bh_enable(); + work_ctx_remove_module = module; + atomic_set(&work_ctx_remove_count, 0); + + err = schedule_on_each_cpu(softirq_lvm_remove_module); + WARN_ON(err); + count += atomic_read(&work_ctx_remove_count); + + __log_info("Unregister module <%s> from pcpu, freed = %d/%d\n", + name, count, nloaded); } - cpus_read_unlock(); - pr_info("Unregistered module <%s> from %d/%d Lua VMs, freed = %d.\n", - name, atomic_read(&module->loaded_count), - atomic_read(&vm_inuse), count); + nloaded = atomic_read(&module->nloaded); + if (count < nloaded) { + /* ditto for the idle 'swapper' tasks */ + cpus_read_lock(); + for_each_possible_cpu(cpu) { + /* TODO: remove 'swapper' tasks Lua VM */ + err = task_call_func(idle_task(cpu), task_remove_module, module); + if (!err) + count++; + } + cpus_read_unlock(); - lua_module_free(module); - return 0; + __log_info("Unregister module <%s> from swapper, freed = %d/%d\n", + name, count, atomic_read(&module->nloaded)); + } + + nloaded = atomic_read(&module->nloaded); + if (count < nloaded && nbusy > 0) { + for (i = 1; i <= 10; i++) { + count += tasks_lvm_remove_module(module, &nbusy); + WARN_ON(count > nloaded); + + __log_info("Unregister module <%s> from task, freed = %d/%d, nbusy = %d, loop = %d\n", + name, count, nloaded, nbusy, i); + + nloaded = atomic_read(&module->nloaded); + if (count == nloaded || nbusy == 0) + break; + + msleep(200 * i); + + nloaded = atomic_read(&module->nloaded); + if (count == nloaded) + break; + } + } + + if (count == nloaded) { + list_del_rcu(&module->list); + lua_module_free(module); + err = 0; + } else { + atomic_set(&module->nloaded, nloaded - count); + module->state = LMS_STATE_ZOMBIE; + err = -EBUSY; + } + mutex_unlock(&modules_mutex); + + pr_info("Unregistered module <%s> from %d/%d Lua VMs, vm_inuse = %d\n", + name, count, nloaded, atomic_read(&vm_inuse)); + + return err; } /*****************************************************************************/ @@ -802,7 +970,7 @@ int lua_lsm_status_show(struct seq_file *m, void *v) total = 0; minimum = INT_MAX; maximum = 0; - spin_lock(&modules_lock); /* TODO: Still need it? */ + mutex_lock(&modules_mutex); read_lock(&tasklist_lock); for_each_process_thread(g, p) { L = lua_lsm_task(p)->L; @@ -824,7 +992,7 @@ int lua_lsm_status_show(struct seq_file *m, void *v) maximum = max(maximum, nbytes); } cpus_read_unlock(); - spin_unlock(&modules_lock); + mutex_unlock(&modules_mutex); avg = inuse ? total / inuse : 0; seq_printf(m, " Mem usage: total %d, min %d, max %d, inuse %d, average %d\n", @@ -882,6 +1050,7 @@ int lua_task_blob_init(struct task_struct *task) void lua_task_blob_free(struct task_struct *task) { struct lua_lsm_task *llt = lua_lsm_task(task); + lua_modules_free(task, llt->L); lua_state_free(llt->L); llt->L = NULL; kvcache_dict_free(&llt->dict); diff --git a/security/lua/lsm.h b/security/lua/lsm.h index 8a86d21d5324..96f9cafc751a 100644 --- a/security/lua/lsm.h +++ b/security/lua/lsm.h @@ -59,18 +59,26 @@ struct lua_module_shdict { char name[]; }; +enum lua_module_state { + LMS_STATE_LIVE, + LMS_STATE_COMING, + LMS_STATE_GOING, + LMS_STATE_ZOMBIE, +}; + struct lua_module { const char *name; const char *author; const char *description; const char *license; int version; + enum lua_module_state state; struct list_head list; __BITMAP_TYPE(, uint32_t, __LL_NR_MAX) hookfuncs; int nhooks; char *chunk; size_t chunk_len; - atomic_t loaded_count; + atomic_t nloaded; struct list_head shdicts; spinlock_t shdict_lock; atomic_t shdict_count; diff --git a/security/lua/lsm_defs.h b/security/lua/lsm_defs.h index bbad812bcc77..e94c562e386d 100644 --- a/security/lua/lsm_defs.h +++ b/security/lua/lsm_defs.h @@ -137,6 +137,8 @@ srcu_read_lock_held(&modules_ss)) { \ if (!__BITMAP_ISSET(__LL_NR_ ## NAME, &module->hookfuncs)) \ continue; \ + if (module->state != LMS_STATE_LIVE) \ + continue; \ lua_getfield(L, -1, module->name); \ lua_getfield(L, -1, #NAME); \ WARN_ON(lua_gettop(L) != 6); \ From c86bc1c4bb9d5f334006315273797ed0e401c939 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Mon, 27 Oct 2025 15:43:17 +0800 Subject: [PATCH 053/110] lua-lsm: Synchronize kvnode garbage collection --- security/lua/kvcache.c | 58 ++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/security/lua/kvcache.c b/security/lua/kvcache.c index 61d56c10a852..76651f19e6b4 100644 --- a/security/lua/kvcache.c +++ b/security/lua/kvcache.c @@ -141,8 +141,9 @@ kvcache_module_link(struct kvcache_dict *dict, } else { atomic_inc(&dict->count); if (module) { + WARN_ON(module->state != LMS_STATE_LIVE); spin_lock(&module->kvnodes_lock); - list_add_tail(&node->modlist, &module->kvnodes); + list_add_tail_rcu(&node->modlist, &module->kvnodes); atomic_inc(&module->kvnodes_count); spin_unlock(&module->kvnodes_lock); } @@ -161,7 +162,7 @@ kvcache_module_unlink_unlocked(struct kvcache_dict *dict, atomic_dec(&dict->count); if (module) { spin_lock(&module->kvnodes_lock); - list_del(&node->modlist); + list_del_rcu(&node->modlist); atomic_dec(&module->kvnodes_count); spin_unlock(&module->kvnodes_lock); } @@ -378,34 +379,38 @@ kvcache_incr(lua_State *L, struct kvcache_dict *dict, struct lua_module *module) return 1; } +/* + * Currently, there's no efficient RCU read-side traversal solution for + * rbtree. Therefore, a spinlock is used to synchronize the release of + * kvcache_nodes along the following two paths: + * - Release the entire kvnodes list when the module is released + * - Release the entire kvcache_dict when the kernel object is released + */ +static DEFINE_SPINLOCK(nodes_gc_lock); + int kvcache_module_nodes_gc(struct lua_module *module) { - struct list_head cleanup_list; struct kvcache_node *node, *tmp; - unsigned long flags; int count, n = 0; - count = atomic_read(&module->kvnodes_count); - - INIT_LIST_HEAD(&cleanup_list); - spin_lock_irqsave(&module->kvnodes_lock, flags); - list_cut_position(&cleanup_list, &module->kvnodes, module->kvnodes.prev); - spin_unlock_irqrestore(&module->kvnodes_lock, flags); - - list_for_each_entry_safe(node, tmp, &cleanup_list, modlist) { - /* TODO: node maybe freed by kvcache_dict_free() */ + spin_lock_bh(&nodes_gc_lock); + list_for_each_entry(node, &module->kvnodes, modlist) { struct kvcache_dict *dict = node->dict; - BUG_ON(!dict); /* * module is set to NULL, so there is no need to remove * node from the module kvnodes queue. */ kvcache_module_unlink(dict, NULL, node); + } + spin_unlock_bh(&nodes_gc_lock); + + list_for_each_entry_safe(node, tmp, &module->kvnodes, modlist) { kvcache_node_drop(node); n += 1; } + count = atomic_read(&module->kvnodes_count); __log_info("module <%s>, kvnodes_count = %d, freed = %d\n", module->name, count, n); WARN_ON(count != n); @@ -416,22 +421,25 @@ int kvcache_module_nodes_gc(struct lua_module *module) void kvcache_dict_free(struct kvcache_dict *dict) { struct kvcache_node *node, *n; - unsigned long flags; + struct lua_module *module; + + spin_lock_bh(&nodes_gc_lock); + RB_FOREACH(node, kvcache, &dict->root) { + module = node->module; + if (module) { + spin_lock(&module->kvnodes_lock); + list_del_rcu(&node->modlist); + atomic_dec(&module->kvnodes_count); + spin_unlock(&module->kvnodes_lock); + } + } + spin_unlock_bh(&nodes_gc_lock); - /* - * Avoid being called in softirq, such as the LSM function - * inode_free_security_rcu will run in the RCU softirq context, - * socket_sock_rcv_skb maybe run in the NET_RX softirq to - * receive data, and file_free_security maybe run in the worker - * thread to delay release the file object via delayed_fput. - */ - write_lock_irqsave(&dict->lock, flags); RB_FOREACH_SAFE(node, kvcache, &dict->root, n) { - kvcache_module_unlink_unlocked(dict, node->module, node); + kvcache_module_unlink_unlocked(dict, NULL, node); kvcache_node_drop(node); } WARN_ON(atomic_read(&dict->count) != 0); - write_unlock_irqrestore(&dict->lock, flags); } void kvcache_dict_init(struct kvcache_dict *dict) From 48d40ee6a3376f97b3fb956f85f535ae323f5a91 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Tue, 28 Oct 2025 17:40:39 +0800 Subject: [PATCH 054/110] lua-lsm: Fixed invalid pointer when multi-unregister busy module --- security/lua/kvcache.c | 5 +++-- security/lua/lsm.c | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/security/lua/kvcache.c b/security/lua/kvcache.c index 76651f19e6b4..33afae8db032 100644 --- a/security/lua/kvcache.c +++ b/security/lua/kvcache.c @@ -405,16 +405,17 @@ int kvcache_module_nodes_gc(struct lua_module *module) } spin_unlock_bh(&nodes_gc_lock); + count = atomic_read(&module->kvnodes_count); list_for_each_entry_safe(node, tmp, &module->kvnodes, modlist) { + list_del(&node->modlist); + atomic_dec(&module->kvnodes_count); kvcache_node_drop(node); n += 1; } - count = atomic_read(&module->kvnodes_count); __log_info("module <%s>, kvnodes_count = %d, freed = %d\n", module->name, count, n); WARN_ON(count != n); - atomic_set(&module->kvnodes_count, count - n); return n; } diff --git a/security/lua/lsm.c b/security/lua/lsm.c index f9a5a54da746..0830324d670b 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -834,6 +834,7 @@ int lua_module_unregister(const char *name) list_del(&shdict->list); kvcache_dict_free(&shdict->dict); kfree(shdict); + atomic_dec(&module->shdict_count); } kvcache_module_nodes_gc(module); @@ -886,7 +887,7 @@ int lua_module_unregister(const char *name) nloaded = atomic_read(&module->nloaded); if (count < nloaded && nbusy > 0) { - for (i = 1; i <= 10; i++) { + for (i = 1; i <= 5; i++) { count += tasks_lvm_remove_module(module, &nbusy); WARN_ON(count > nloaded); @@ -897,7 +898,7 @@ int lua_module_unregister(const char *name) if (count == nloaded || nbusy == 0) break; - msleep(200 * i); + msleep(500 * i); nloaded = atomic_read(&module->nloaded); if (count == nloaded) @@ -905,12 +906,11 @@ int lua_module_unregister(const char *name) } } - if (count == nloaded) { + if (atomic_sub_return(count, &module->nloaded) == 0) { list_del_rcu(&module->list); lua_module_free(module); err = 0; } else { - atomic_set(&module->nloaded, nloaded - count); module->state = LMS_STATE_ZOMBIE; err = -EBUSY; } From cc7e8fbaf8b4c3b94d9c92d7886183a304a36677 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Mon, 10 Nov 2025 12:13:07 +0800 Subject: [PATCH 055/110] lua-lsm: Avoid recursive calls to the capable --- security/lua/lua_capability.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/security/lua/lua_capability.c b/security/lua/lua_capability.c index bfe783769588..946abab92d9f 100644 --- a/security/lua/lua_capability.c +++ b/security/lua/lua_capability.c @@ -154,7 +154,8 @@ static int capability_cap_full(lua_State *L) static int capability_capable(lua_State *L) { int cap = luaL_checkinteger(L, 1); - lua_pushboolean(L, capable(cap)); + int err = cap_capable(current_cred(), &init_user_ns, cap, CAP_OPT_NONE); + lua_pushboolean(L, err == 0); return 1; } From de28578d4e60262b9eee202d44f768841008cc24 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Mon, 10 Nov 2025 17:51:52 +0800 Subject: [PATCH 056/110] lua-lsm: api - exepath/cmdline and inode mode --- security/lua/auxlib.c | 31 +++++++++++++ security/lua/auxlib.h | 13 ++++++ security/lua/lsm.h | 12 ----- security/lua/lua_fs.c | 98 +++++++++++++++++++++++++++++++++++---- security/lua/lua_kernel.c | 36 +++++++++++++- 5 files changed, 167 insertions(+), 23 deletions(-) diff --git a/security/lua/auxlib.c b/security/lua/auxlib.c index 10a8319a566f..407e38b8626b 100644 --- a/security/lua/auxlib.c +++ b/security/lua/auxlib.c @@ -10,6 +10,7 @@ #include "debug.h" #include #include +#include #include #include #include @@ -300,3 +301,33 @@ void setconst(lua_State *L, const struct const_value *cv) lua_settable(L, -3); } } + +int aux_file_path(lua_State *L, struct file *filp) +{ + char buffer[256]; + char *buf = NULL; + char *path; + int nres; + + path = file_path(filp, buffer, sizeof(buffer)); + if (IS_ERR(path)) { + char *buf = kmalloc(PATH_MAX, lua_lsm_gfp()); + if (!buf) { + lua_pushnil(L); + lua_pushinteger(L, -ENOMEM); + return 2; + } + path = file_path(filp, buf, PATH_MAX); + } + if (IS_ERR(path)) { + lua_pushnil(L); + lua_pushinteger(L, PTR_ERR(path)); + nres = 2; + } else { + lua_pushstring(L, path); + nres = 1; + } + if (buf) + kfree(buf); + return nres; +} diff --git a/security/lua/auxlib.h b/security/lua/auxlib.h index fb01da9958fb..a78cbfac1cf1 100644 --- a/security/lua/auxlib.h +++ b/security/lua/auxlib.h @@ -11,6 +11,17 @@ #include #include +static inline gfp_t lua_lsm_gfp(void) +{ + /* + * Sleeping function is not allowed in atomic, rcu and + * softirq context. + */ + if (in_atomic() || rcu_preempt_depth() > 0) + return GFP_ATOMIC; + else + return GFP_NOFS; +} #define luaL_newlibtable(L, l) \ (lua_createtable((L), 0, sizeof((l)) / sizeof(*(l)) - 1)) @@ -63,4 +74,6 @@ struct const_value { void setconst(lua_State *L, const struct const_value *cv); +int aux_file_path(lua_State *L, struct file *filp); + #endif /* ! _SECURITY_LUA_LSM_AUXLIB_H */ diff --git a/security/lua/lsm.h b/security/lua/lsm.h index 96f9cafc751a..6233a4b502e7 100644 --- a/security/lua/lsm.h +++ b/security/lua/lsm.h @@ -107,18 +107,6 @@ int lua_lsm_status_show(struct seq_file *m, void *v); int lsmhook_stat_show(struct seq_file *m, void *v); #endif -static inline gfp_t lua_lsm_gfp(void) -{ - /* - * Sleeping function is not allowed in atomic, rcu and - * softirq context. - */ - if (in_atomic() || rcu_preempt_depth() > 0) - return GFP_ATOMIC; - else - return GFP_NOFS; -} - extern struct lsm_blob_sizes lua_lsm_blob_sizes; struct lua_lsm_task { diff --git a/security/lua/lua_fs.c b/security/lua/lua_fs.c index 6ec4a9f8017b..b6998d1b9912 100644 --- a/security/lua/lua_fs.c +++ b/security/lua/lua_fs.c @@ -130,10 +130,98 @@ static int fs_inode_size(lua_State *L) return 1; } +/* + * file_type = inode:fmt() + * bool = inode:fmt('sock') + */ +static int fs_inode_fmt(lua_State *L) +{ + static const struct cflag_opt opts[] = { + { "sock", S_IFSOCK }, + { "lnk", S_IFLNK }, + { "reg", S_IFREG }, + { "blk", S_IFBLK }, + { "dir", S_IFDIR }, + { "chr", S_IFCHR }, + { "fifo", S_IFIFO }, + { NULL, 0 } + }; + struct inode *inode = toinode(L, 1); + int top = lua_gettop(L); + int i; + if (top == 1) { + for (i = 0; opts[i].name; i++) { + if ((inode->i_mode & S_IFMT) != opts[i].flag) + continue; + lua_pushstring(L, opts[i].name); + return 1; + } + } else if (top == 2) { + const char *s = luaL_checkstring(L, 2); + for (i = 0; opts[i].name; i++) { + if (strcasecmp(s, opts[i].name) != 0) + continue; + lua_pushboolean(L, + (inode->i_mode & S_IFMT) == opts[i].flag); + return 1; + } + } + return 0; +} + +/* + * permissions_table = inode:mode() + * bool = inode:mode('suid') + * bool = inode:mode(true, 'suid', 'xgrp', 'xoth') + */ +static int fs_inode_mode(lua_State *L) +{ + static const struct cflag_opt opts[] = { + { "suid", S_ISUID }, + { "sgid", S_ISGID }, + { "vtx", S_ISVTX }, + { "rusr", S_IRUSR }, + { "wusr", S_IWUSR }, + { "xusr", S_IXUSR }, + { "rgrp", S_IRGRP }, + { "wgrp", S_IWGRP }, + { "xgrp", S_IXGRP }, + { "roth", S_IROTH }, + { "woth", S_IWOTH }, + { "xoth", S_IXOTH }, + { NULL, 0 } + }; + struct inode *inode = toinode(L, 1); + int top = lua_gettop(L); + if (top >= 2) { + int start = (top == 2) ? 2 : 3; + int and = lua_isboolean(L, 2) && lua_toboolean(L, 2); + unsigned int flags = tocflags(L, start, top, opts, 0); + unsigned int res = inode->i_mode & flags; + lua_pushboolean(L, and ? res == flags : (int)res); + return 1; + } else if (top == 1) { + table_fromopts(L, opts, 0, (unsigned int)inode->i_mode); + return 1; + } + return 0; +} + +static int fs_inode_ids(lua_State *L) +{ + struct inode *inode = toinode(L, 1); + lua_pushinteger(L, inode->i_uid.val); + lua_pushinteger(L, inode->i_gid.val); + return 2; +} + static const luaL_Reg fs_inode_meth[] = { { "ino", fs_inode_ino }, { "xattr", fs_inode_xattr }, { "size", fs_inode_size }, + { "fmt", fs_inode_fmt }, + { "mode", fs_inode_mode }, + { "ids", fs_inode_ids }, { NULL, NULL } }; @@ -149,15 +237,7 @@ static int fs_file_fput(lua_State *L) static int fs_file_path(lua_State *L) { struct file *file = tofile(L, 1); - char buffer[FS_PATH_LEN]; - char *path = file_path(file, buffer, sizeof(buffer)); - if (IS_ERR(path)) { - lua_pushnil(L); - lua_pushinteger(L, PTR_ERR(path)); - return 2; - } - lua_pushstring(L, path); - return 1; + return aux_file_path(L, file); } static int fs_file_dentry(lua_State *L) diff --git a/security/lua/lua_kernel.c b/security/lua/lua_kernel.c index c2c171272e50..4b2c49e9c18e 100644 --- a/security/lua/lua_kernel.c +++ b/security/lua/lua_kernel.c @@ -101,8 +101,8 @@ static int kernel_cred_cap_ambient(lua_State *L) static int kernel_cred_securebits(lua_State *L) { static const struct cflag_opt opts[] = { - { "noroot", SECBIT_NOROOT }, - { "no_setuid_fixup", SECBIT_NO_SETUID_FIXUP }, + { "noroot", SECBIT_NOROOT }, + { "no_setuid_fixup", SECBIT_NO_SETUID_FIXUP }, { "keep_caps", SECBIT_KEEP_CAPS }, { "no_cap_ambient_raise", SECBIT_NO_CAP_AMBIENT_RAISE }, { "exec_restrict_file", SECBIT_EXEC_RESTRICT_FILE }, @@ -219,10 +219,40 @@ static int kernel_task_exe_file(lua_State *L) struct file *exe_file = get_task_exe_file(task); if (exe_file == NULL) return 0; + /* XXX: fput() must be called after use. */ *newfile(L) = exe_file; return 1; } +static int kernel_task_exepath(lua_State *L) +{ + struct task_struct *task = totask(L, 1); + struct file *file; + int nres = 0; + if (task == current) { + struct mm_struct *mm = current->mm; + if (!mm) + return 0; + file = get_mm_exe_file(mm); + } else { + file = get_task_exe_file(task); + } + if (file) { + nres = aux_file_path(L, file); + fput(file); + } + return nres; +} + +static int kernel_task_cmdline(lua_State *L) +{ + struct task_struct *task = totask(L, 1); + char *cmdline = kstrdup_quotable_cmdline(task, lua_lsm_gfp()); + lua_pushstring(L, cmdline); + kfree(cmdline); + return 1; +} + static const luaL_Reg task_meth[] = { { "pids", kernel_task_pids }, { "cred", kernel_task_cred }, @@ -234,6 +264,8 @@ static const luaL_Reg task_meth[] = { { "ptrace_parent", kernel_task_ptrace_parent }, { "is_idle", kernel_task_is_idle }, { "exe_file", kernel_task_exe_file }, + { "exepath", kernel_task_exepath }, + { "cmdline", kernel_task_cmdline }, { NULL, NULL } }; From a77230248f5ce2d4c81fd5664e27313b885978d4 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Fri, 14 Nov 2025 18:30:07 +0800 Subject: [PATCH 057/110] lua-lsm: pretty show lsm_funcs & module --- security/lua/lsm.c | 17 ++++++++++------- security/lua/lsm.h | 5 +++-- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/security/lua/lsm.c b/security/lua/lsm.c index 0830324d670b..1b57aecf9788 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -930,16 +930,19 @@ int modules_show(struct seq_file *m, void *v) int idx; seq_printf(m, "modules for lua-lsm\n"); - seq_printf(m, "%-12s %-10s %6s %6s %-48s\n", - "name", "license", "size", "nhooks", "author"); + seq_printf(m, "%-12s %-10s %6s %4s %5s %6s %6s %-34s\n", + "name", "license", "size", "nlsm", + "nload", "shdict", "kvnode", "author"); seq_printf(m, "%s\n", TABLINE); idx = srcu_read_lock(&modules_ss); list_for_each_entry_srcu(module, &lsm_modules, list, srcu_read_lock_held(&modules_ss)) { - seq_printf(m, "%-12s %-10s %6zu %6d %-48s\n", + seq_printf(m, "%-12s %-10s %6zu %4d %5d %6d %6d %-34s\n", module->name, module->license, module->chunk_len, - module->nhooks, module->author); + module->nhooks, atomic_read(&module->nloaded), + atomic_read(&module->shdict_count), + atomic_read(&module->kvnodes_count), module->author); } srcu_read_unlock(&modules_ss, idx); return 0; @@ -1012,15 +1015,15 @@ int lsmhook_stat_show(struct seq_file *m, void *v) int i = 1; seq_printf(m, "stats for lua-lsm (ns)\n"); - seq_printf(m, "%3s %-28s %6s %8s %15s %10s %12s\n", - "num", "name", "nhooks", "count", "total", "average", "maxtime"); + seq_printf(m, "%3s %-28s %4s %12s %15s %10s %12s\n", + "num", "name", "nlsm", "count", "total", "average", "maxtime"); seq_printf(m, "%s\n", TABLINE); for (stat = lua_lsm_hook_stats; stat->name; stat++) { int n = atomic_read(&stat->count); s64 total = atomic64_read(&stat->time); s64 maxtime = atomic64_read(&stat->maxtime); - seq_printf(m, "%3d %-28s %6d %8d %15llu %10llu %12llu\n", + seq_printf(m, "%3d %-28s %4d %12d %15llu %10llu %12llu\n", i++, stat->name, atomic_read(&stat->nhooks), n, total, n ? total / n : 0, maxtime); } diff --git a/security/lua/lsm.h b/security/lua/lsm.h index 6233a4b502e7..9bf4438acbb1 100644 --- a/security/lua/lsm.h +++ b/security/lua/lsm.h @@ -91,8 +91,9 @@ extern struct list_head lsm_modules; extern struct srcu_struct modules_ss; -#define TABLINE "--------------------------------------------" \ - "--------------------------------------------" +#define TABLINE \ + "---------------------------------------------" \ + "---------------------------------------------" lua_State *lvm_get(void); void lvm_put(lua_State *L); From fe489b2a4b625377b2e1f29fdd350049ed7547c7 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Thu, 27 Nov 2025 10:49:54 +0800 Subject: [PATCH 058/110] lua-lsm: Make tostring overloadable --- security/lua/auxlib.c | 59 +++++++++++++++-- security/lua/auxlib.h | 1 + security/lua/lua_fs.c | 129 ++++++++++++++++++++++++++------------ security/lua/lua_kernel.c | 8 +++ security/lua/lua_object.h | 9 ++- 5 files changed, 158 insertions(+), 48 deletions(-) diff --git a/security/lua/auxlib.c b/security/lua/auxlib.c index 407e38b8626b..a58a74db21c5 100644 --- a/security/lua/auxlib.c +++ b/security/lua/auxlib.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -309,19 +310,67 @@ int aux_file_path(lua_State *L, struct file *filp) char *path; int nres; + if (unlikely(!filp || !current->fs)) { + lua_pushnil(L); + lua_pushstring(L, errname(-ESRCH)); + return 2; + } + + if (unlikely(filp->f_path.dentry == NULL)) { + lua_pushnil(L); + lua_pushstring(L, errname(-ENOENT)); + return 2; + } + path = file_path(filp, buffer, sizeof(buffer)); - if (IS_ERR(path)) { - char *buf = kmalloc(PATH_MAX, lua_lsm_gfp()); - if (!buf) { + if (PTR_ERR(path) == -ENAMETOOLONG) { + buf = kmalloc(PATH_MAX, lua_lsm_gfp()); + if (buf == NULL) { lua_pushnil(L); - lua_pushinteger(L, -ENOMEM); + lua_pushstring(L, errname(-ENOMEM)); return 2; } path = file_path(filp, buf, PATH_MAX); } if (IS_ERR(path)) { lua_pushnil(L); - lua_pushinteger(L, PTR_ERR(path)); + lua_pushstring(L, errname(PTR_ERR(path))); + nres = 2; + } else { + lua_pushstring(L, path); + nres = 1; + } + if (buf) + kfree(buf); + return nres; +} + +int aux_dentry_path(lua_State *L, struct dentry *dentry, int rawpath) +{ + char buffer[256]; + char *buf = NULL; + char *path; + int nres; + + if (rawpath) + path = dentry_path_raw(dentry, buffer, sizeof(buffer)); + else + path = dentry_path(dentry, buffer, sizeof(buffer)); + if (PTR_ERR(path) == -ENAMETOOLONG) { + buf = kmalloc(PATH_MAX, lua_lsm_gfp()); + if (buf == NULL) { + lua_pushnil(L); + lua_pushstring(L, errname(-ENOMEM)); + return 2; + } + if (rawpath) + path = dentry_path_raw(dentry, buf, PATH_MAX); + else + path = dentry_path(dentry, buf, PATH_MAX); + } + if (IS_ERR(path)) { + lua_pushnil(L); + lua_pushstring(L, errname(PTR_ERR(path))); nres = 2; } else { lua_pushstring(L, path); diff --git a/security/lua/auxlib.h b/security/lua/auxlib.h index a78cbfac1cf1..0d2a465784f0 100644 --- a/security/lua/auxlib.h +++ b/security/lua/auxlib.h @@ -75,5 +75,6 @@ struct const_value { void setconst(lua_State *L, const struct const_value *cv); int aux_file_path(lua_State *L, struct file *filp); +int aux_dentry_path(lua_State *L, struct dentry *dentry, int rawpath); #endif /* ! _SECURITY_LUA_LSM_AUXLIB_H */ diff --git a/security/lua/lua_fs.c b/security/lua/lua_fs.c index b6998d1b9912..18d92f160af8 100644 --- a/security/lua/lua_fs.c +++ b/security/lua/lua_fs.c @@ -18,7 +18,6 @@ #include "kvcache.h" #include "lua_object.h" -#define FS_PATH_LEN 256 /********************************** util **********************************/ @@ -73,36 +72,39 @@ static int fs_dentry_backing_inode(lua_State *L) return 1; } +static int fs_dentry_xattr(lua_State *L) +{ + struct dentry *dentry = todentry(L, 1); + struct inode *inode = toinode(L, 2); + const char *name = luaL_checkstring(L, 3); + return fs_xattr(L, dentry, inode, name); +} + static int fs_dentry_path(lua_State *L) { struct dentry *dentry = todentry(L, 1); - int use_path = lua_toboolean(L, 2); - char buffer[FS_PATH_LEN]; - char *path; - if (use_path) - path = dentry_path(dentry, buffer, sizeof(buffer)); - else - path = dentry_path_raw(dentry, buffer, sizeof(buffer)); - if (path == NULL) - return 0; - lua_pushstring(L, path); - return 1; + int rawpath = lua_toboolean(L, 2); + return aux_dentry_path(L, dentry, rawpath); } -static int fs_dentry_xattr(lua_State *L) +static int meth_dentry_tostring(lua_State *L) { struct dentry *dentry = todentry(L, 1); - struct inode *inode = toinode(L, 2); - const char *name = luaL_checkstring(L, 3); - return fs_xattr(L, dentry, inode, name); + int nres = aux_dentry_path(L, dentry, 0); + if (nres == 2) + lua_pushfstring(L, "dentry ", lua_tostring(L, -1)); + else + lua_pushfstring(L, "dentry '%s'", lua_tostring(L, -1)); + return 1; } static const luaL_Reg fs_dentry_meth[] = { { "dget", fs_dentry_dget }, { "dput", fs_dentry_dput }, { "backing_inode", fs_dentry_backing_inode }, - { "path", fs_dentry_path }, { "xattr", fs_dentry_xattr }, + { "path", fs_dentry_path }, + { "__tostring", meth_dentry_tostring }, { NULL, NULL } }; @@ -131,10 +133,10 @@ static int fs_inode_size(lua_State *L) } /* - * file_type = inode:fmt() - * bool = inode:fmt('sock') + * ftype = inode:filetype() + * bool = inode:filetype('sock') */ -static int fs_inode_fmt(lua_State *L) +static int fs_inode_filetype(lua_State *L) { static const struct cflag_opt opts[] = { { "sock", S_IFSOCK }, @@ -215,13 +217,23 @@ static int fs_inode_ids(lua_State *L) return 2; } +static int meth_inode_tostring(lua_State *L) +{ + struct inode *inode = toinode(L, 1); + char buffer[128]; + int l = snprintf(buffer, sizeof(buffer), "inode [%lu]", inode->i_ino); + lua_pushlstring(L, buffer, l); + return 1; +} + static const luaL_Reg fs_inode_meth[] = { - { "ino", fs_inode_ino }, - { "xattr", fs_inode_xattr }, - { "size", fs_inode_size }, - { "fmt", fs_inode_fmt }, - { "mode", fs_inode_mode }, - { "ids", fs_inode_ids }, + { "ino", fs_inode_ino }, + { "xattr", fs_inode_xattr }, + { "size", fs_inode_size }, + { "filetype", fs_inode_filetype }, + { "mode", fs_inode_mode }, + { "ids", fs_inode_ids }, + { "__tostring", meth_inode_tostring }, { NULL, NULL } }; @@ -234,12 +246,6 @@ static int fs_file_fput(lua_State *L) return 0; } -static int fs_file_path(lua_State *L) -{ - struct file *file = tofile(L, 1); - return aux_file_path(L, file); -} - static int fs_file_dentry(lua_State *L) { const struct file *file = tofile(L, 1); @@ -303,14 +309,32 @@ static int fs_file_fmode(lua_State *L) return 0; } +static int fs_file_path(lua_State *L) +{ + struct file *file = tofile(L, 1); + return aux_file_path(L, file); +} + +static int meth_file_tostring(lua_State *L) +{ + struct file *file = tofile(L, 1); + int nres = aux_file_path(L, file); + if (nres == 2) + lua_pushfstring(L, "file ", lua_tostring(L, -1)); + else + lua_pushfstring(L, "file '%s'", lua_tostring(L, -1)); + return 1; +} + static const luaL_Reg fs_file_meth[] = { - { "fput", fs_file_fput }, - { "path", fs_file_path }, - { "dentry", fs_file_dentry }, - { "inode", fs_file_inode }, - { "xattr", fs_file_xattr }, - { "size", fs_file_size }, - { "fmode", fs_file_fmode }, + { "fput", fs_file_fput }, + { "dentry", fs_file_dentry }, + { "inode", fs_file_inode }, + { "xattr", fs_file_xattr }, + { "size", fs_file_size }, + { "fmode", fs_file_fmode }, + { "path", fs_file_path }, + { "__tostring", meth_file_tostring }, { NULL, NULL } }; @@ -369,10 +393,22 @@ static int fs_path_eq(lua_State *L) return 1; } +static int meth_path_tostring(lua_State *L) +{ + struct path *path = topath(L, 1); + int nres = aux_dentry_path(L, path->dentry, 0); + if (nres == 2) + lua_pushfstring(L, "path ", lua_tostring(L, -1)); + else + lua_pushfstring(L, "path '%s'", lua_tostring(L, -1)); + return 1; +} + static const luaL_Reg fs_path_meth[] = { { "vfsmount", fs_path_vfsmount }, { "dentry", fs_path_dentry }, { "__eq", fs_path_eq }, + { "__tostring", meth_path_tostring }, { NULL, NULL } }; @@ -396,7 +432,19 @@ static int fs_superblock_root(lua_State *L) static int fs_superblock_fstype_name(lua_State *L) { struct super_block *sb = tosuperblock(L, 1); - lua_pushstring(L, sb->s_type->name); + struct file_system_type *type = sb->s_type; + if (type) + lua_pushstring(L, type->name); + else + lua_pushnil(L); + return 1; +} + +static int meth_superblock_tostring(lua_State *L) +{ + struct super_block *sb = tosuperblock(L, 1); + struct file_system_type *type = sb->s_type; + lua_pushfstring(L, "superblock '%s'", type ? type->name : ""); return 1; } @@ -404,6 +452,7 @@ static const luaL_Reg fs_superblock_meth[] = { { "magic", fs_superblock_magic }, { "root", fs_superblock_root }, { "fstype_name", fs_superblock_fstype_name }, + { "__tostring", meth_superblock_tostring }, { NULL, NULL } }; diff --git a/security/lua/lua_kernel.c b/security/lua/lua_kernel.c index 4b2c49e9c18e..08596dfd48b2 100644 --- a/security/lua/lua_kernel.c +++ b/security/lua/lua_kernel.c @@ -253,6 +253,13 @@ static int kernel_task_cmdline(lua_State *L) return 1; } +static int meth_task_tostring(lua_State *L) +{ + struct task_struct *task = totask(L, 1); + lua_pushfstring(L, "task '%s'", task->comm); + return 1; +} + static const luaL_Reg task_meth[] = { { "pids", kernel_task_pids }, { "cred", kernel_task_cred }, @@ -266,6 +273,7 @@ static const luaL_Reg task_meth[] = { { "exe_file", kernel_task_exe_file }, { "exepath", kernel_task_exepath }, { "cmdline", kernel_task_cmdline }, + { "__tostring", meth_task_tostring }, { NULL, NULL } }; diff --git a/security/lua/lua_object.h b/security/lua/lua_object.h index ceb93a41bdef..a466abdd8f8b 100644 --- a/security/lua/lua_object.h +++ b/security/lua/lua_object.h @@ -66,8 +66,9 @@ { "__tostring", class ## _ ## name ## _tostring }, \ { NULL, NULL } \ }; \ - createmeta(L, METHOD_NAME(class, name), funcs, 0, 0); \ - luaL_register(L, NULL, object_meth); \ + createmeta(L, METHOD_NAME(class, name), object_meth, 0, 0); \ + if (funcs) \ + luaL_register(L, NULL, funcs); \ lua_pop(L, 1); \ } @@ -76,9 +77,11 @@ static inline void create_ ## name ## _meta(lua_State *L, \ const luaL_Reg *funcs) \ { \ - createmeta(L, METHOD_NAME(class, name), funcs, 1, 0); \ + createmeta(L, METHOD_NAME(class, name), NULL, 1, 0); \ lua_pushcfunction(L, class ## _ ## name ## _tostring); \ lua_setfield(L, -2, "__tostring"); /* mt.__tostring = func */ \ + if (funcs) \ + luaL_register(L, NULL, funcs); \ lua_pop(L, 1); \ } From d69f677ce6099842e33e1e0cf1cb577b5186f493 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Fri, 28 Nov 2025 14:57:50 +0800 Subject: [PATCH 059/110] lua-lsm: add extra two metatables for raw and gc object --- security/lua/auxlib.c | 171 +++++++++++++++++++++++++--------- security/lua/auxlib.h | 13 ++- security/lua/kvcache.c | 25 +++-- security/lua/lua_capability.c | 2 +- security/lua/lua_fs.c | 34 +++---- security/lua/lua_kernel.c | 16 ++-- security/lua/lua_net.c | 8 +- security/lua/lua_object.h | 103 +++++++++++--------- 8 files changed, 242 insertions(+), 130 deletions(-) diff --git a/security/lua/auxlib.c b/security/lua/auxlib.c index a58a74db21c5..5a645805a6a3 100644 --- a/security/lua/auxlib.c +++ b/security/lua/auxlib.c @@ -19,54 +19,85 @@ #include "auxlib.h" -void lua_stack_dump(lua_State *L) -{ - int top = lua_gettop(L); - int i; - - __log_info("-------- stack dump start --------\n"); - for (i = 1; i <= top; i++) { - int type = lua_type(L, i); - const char * __maybe_unused name = lua_typename(L, type); - - if (type == LUA_TSTRING) - __log_info(" %2d [%s] %s\n", i, name, lua_tostring(L, i)); - else - __log_info(" %2d [%s]\n", i, name); - } - __log_info("-------- stack dump end --------\n"); -} - -void lua_table_dump(lua_State *L, const char *prefix) +void lua_table_dump(lua_State *L, int idx, int level, int max) { int i = 1; - if (!lua_istable(L, -1)) { - __log_err("%s: not a table, type = %s\n", - prefix, luaL_typename(L, -1)); + if (idx < 0) + idx = lua_gettop(L) + 1 + idx; + if (!lua_istable(L, idx)) { + pr_err("%*s \n", + level * 4, "", luaL_typename(L, -1)); + return; + } + if (level > 10) { + pr_err("%*s \n", + level * 4, "", level); return; } - __log_info("-------- %s: table dump start --------\n", prefix); /* table is in the stack at index 't' */ lua_pushnil(L); /* first key */ - while (lua_next(L, -2) != 0) { - /* uses 'key' (at index -2) and 'value' (at index -1) */ - __log_info(" %2d: [%s] %s\t- [%s] %s\n", i, - luaL_typename(L, -2), lua_tostring(L, -2) ?: "(null)", - luaL_typename(L, -1), lua_tostring(L, -1) ?: "(null)"); - /* removes 'value'; keeps 'key' for next iteration */ - lua_pop(L, 1); + while (lua_next(L, idx) != 0) { + if (max != -1 && i > max) { + pr_info("%*s ... ...\n", level * 4, ""); + lua_pop(L, 2); + break; + } + + /* To avoid changing the key in lua_tostring */ + lua_pushvalue(L, -2); + /* stack: [key, value, key2] */ + switch (lua_type(L, -2)) { + case LUA_TTABLE: + pr_info("%*s %2d: [%s] %s\n", level * 4, "", i, + luaL_typename(L, -1), lua_tostring(L, -1) ?: "(null)"); + lua_table_dump(L, -2, level + 1, 5); + break; + default: + /* uses 'key' (at index -2) and 'value' (at index -1) */ + pr_info("%*s %2d: [%s] %s\t- [%s] %s\n", + level * 4, "", i, + luaL_typename(L, -1), lua_tostring(L, -1) ?: "(null)", + luaL_typename(L, -2), lua_tostring(L, -2) ?: "(null)"); + break; + } + + /* removes 'value' and 'key2'; keeps 'key' for next iteration */ + lua_pop(L, 2); i += 1; } - __log_info("-------- %s: table dump end --------\n", prefix); +} + +void lua_stack_dump(lua_State *L) +{ + int top = lua_gettop(L); + int i; + + for (i = 1; i <= top; i++) { + switch (lua_type(L, i)) { + case LUA_TTABLE: + pr_info(" %2d: [table]\n", i); + lua_table_dump(L, i, 1, 6); + break; + default: + lua_pushvalue(L, i); + pr_info(" %2d: [%s] %s\n", i, + luaL_typename(L, -1), lua_tostring(L, -1) ?: "(null)"); + lua_pop(L, 1); + break; + } + } } /* borrowed from lua.c */ int lua_traceback(lua_State *L) { - pr_err("@_@ LuaVM: top = %d\n", lua_gettop(L)); + pr_err("@_@ LuaVM traceback: %s [%d] Lua stacktop = %d\n", + current->comm, task_pid_nr(current), lua_gettop(L)); if (!lua_isstring(L, 1)) /* 'message' not a string? */ return 1; /* keep it intact */ pr_err("@_@ LuaVM: %s\n", lua_tostring(L, -1)); + + /* Lua stack */ lua_getfield(L, LUA_GLOBALSINDEX, "debug"); if (!lua_istable(L, -1)) { lua_pop(L, 1); @@ -82,9 +113,10 @@ int lua_traceback(lua_State *L) lua_call(L, 2, 1); /* call debug.traceback */ pr_err("@_@ LuaVM stack:\n%s\n", lua_tostring(L, -1)); - __log_err("------------------ C stack dump start ------------------\n"); + pr_err("----------------------------------------\n"); + /* C stack */ dump_stack(); - __log_err("------------------ C stack dump end ------------------\n"); + pr_err("----------------------------------------\n"); return 1; } @@ -263,18 +295,28 @@ void **newcptr(lua_State *L, const char *metatable) return p; } -void createmeta(lua_State *L, const char *name, - const luaL_Reg *meth, int index, int pop) +void createmeta(lua_State *L, const char *tname, const char *name, + const luaL_Reg *meth, const luaL_Reg *base, int pop) { - luaL_newmetatable(L, name); - lua_pushstring(L, "cannot get a protected metatable"); - lua_setfield(L, -2, "__metatable"); /* metatable.__metatable = msg */ - if (index) { - lua_pushvalue(L, -1); - lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */ + luaL_newmetatable(L, tname); + /* metatable.__metatable = error_message */ + lua_pushstring(L, "cannot set a protected metatable"); + lua_setfield(L, -2, "__metatable"); + if (name) { + /* metatable.__name = name */ + lua_pushstring(L, name); + lua_setfield(L, -2, "__name"); } + + /* metatable.__index = metatable */ + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + if (base) + luaL_register(L, NULL, base); + if (meth) luaL_register(L, NULL, meth); + if (pop) lua_pop(L, 1); } @@ -294,6 +336,51 @@ void *checkudata(lua_State *L, int ud, const char *name) return NULL; } +/* + * meta meta + * [ [ gc ] ------> regular ] ------> raw + */ +void createmeta3(lua_State *L, const char *name, const luaL_Reg *base, + const char *tname_gc, const luaL_Reg *funcs_gc, + const char *tname, const luaL_Reg *funcs, + const char *tname_raw, const luaL_Reg *funcs_raw) +{ + if (funcs_gc && funcs) + createmeta(L, tname_gc, name, funcs_gc, base, 0); + + /* Always create regular metatable and raw metatable. */ + createmeta(L, tname, name, funcs, base, 0); + createmeta(L, tname_raw, name, funcs_raw, base, 0); + lua_setmetatable(L, -2); + + if (funcs_gc && funcs) + lua_setmetatable(L, -2); + + lua_pop(L, 1); +} + +void *checkudata3(lua_State *L, int ud, const char *tname) +{ + int idx, n; + void *p = lua_touserdata(L, ud); + if (p == NULL) + return NULL; + + luaL_getmetatable(L, tname); + idx = ud; + for (n = 1; lua_getmetatable(L, idx); n++) { + if (lua_rawequal(L, -1, -1 - n)) { + lua_pop(L, n + 1); + return p; + } + idx = -1; + } + lua_pop(L, n); + + luaL_typerror(L, ud, tname); + return NULL; +} + void setconst(lua_State *L, const struct const_value *cv) { for ( ; cv->name; cv++) { diff --git a/security/lua/auxlib.h b/security/lua/auxlib.h index 0d2a465784f0..39b83eecb7ae 100644 --- a/security/lua/auxlib.h +++ b/security/lua/auxlib.h @@ -31,8 +31,8 @@ static inline gfp_t lua_lsm_gfp(void) /* debug functions */ +void lua_table_dump(lua_State *L, int idx, int level, int max); void lua_stack_dump(lua_State *L); -void lua_table_dump(lua_State *L, const char *prefix); /* aux functions */ @@ -61,10 +61,17 @@ void table_fromopts(lua_State *L, const struct cflag_opt *opts, unsigned int bitfield, unsigned int mask); void **newcptr(lua_State *L, const char *metatable); -void createmeta(lua_State *L, const char *name, - const luaL_Reg *meth, int index, int pop); +void createmeta(lua_State *L, const char *tname, const char *name, + const luaL_Reg *meth, const luaL_Reg *base, int pop); void *checkudata(lua_State *L, int ud, const char *name); +void createmeta3(lua_State *L, const char *name, const luaL_Reg *base, + const char *tname_gc, const luaL_Reg *funcs_gc, + const char *tname, const luaL_Reg *funcs, + const char *tname_raw, const luaL_Reg *funcs_raw); +void *checkudata3(lua_State *L, int ud, const char *tname); + + struct const_value { const char *name; intptr_t value; diff --git a/security/lua/kvcache.c b/security/lua/kvcache.c index 33afae8db032..5583e2eadef1 100644 --- a/security/lua/kvcache.c +++ b/security/lua/kvcache.c @@ -517,10 +517,13 @@ int lua_object_incr(lua_State *L, struct kvcache_dict *dict) * if not mt then * return nil * end - * local v = mt[key] - * if v then - * return v - * end + * repeat + * local v = rawget(mt, key) + * if v then + * return v + * end + * mt = getmetatable(mt) + * until mt == nil * local module = fenv.MODULE_KEY * return C.kvcache_get(L, object.kvcache, module, key) * end @@ -531,10 +534,14 @@ int lua_object_index(lua_State *L, struct kvcache_dict *dict) __log_err("NO metatable for userdata\n"); return 0; } - lua_pushvalue(L, 2); - lua_rawget(L, -2); /* metatable[key] */ - if (!lua_isnoneornil(L, -1)) - return 1; + + do { + lua_pushvalue(L, 2); + lua_rawget(L, -2); /* metatable[key] */ + if (!lua_isnil(L, -1)) + return 1; + lua_pop(L, 1); + } while (lua_getmetatable(L, -1)); return lua_object_get(L, dict); } @@ -623,6 +630,6 @@ static const luaL_Reg shdict_meth[] = { int shdict_init(lua_State *L) { - createmeta(L, METH_SHARED_DICT, shdict_meth, 1, 1); + createmeta(L, METH_SHARED_DICT, "shdict", shdict_meth, NULL, 1); return 0; } diff --git a/security/lua/lua_capability.c b/security/lua/lua_capability.c index 946abab92d9f..0fe15c8268fa 100644 --- a/security/lua/lua_capability.c +++ b/security/lua/lua_capability.c @@ -169,7 +169,7 @@ static const luaL_Reg capabilitylib[] = { int luaopen_capability(lua_State *L) { luaL_newlib(L, capabilitylib); - create_cap_meta(L, cap_meth); + create_cap_meta(L, cap_meth, NULL); setconst(L, capabilities); return 1; } diff --git a/security/lua/lua_fs.c b/security/lua/lua_fs.c index 18d92f160af8..920ae4ba61fe 100644 --- a/security/lua/lua_fs.c +++ b/security/lua/lua_fs.c @@ -92,9 +92,9 @@ static int meth_dentry_tostring(lua_State *L) struct dentry *dentry = todentry(L, 1); int nres = aux_dentry_path(L, dentry, 0); if (nres == 2) - lua_pushfstring(L, "dentry ", lua_tostring(L, -1)); + lua_pushfstring(L, "dentry: ", lua_tostring(L, -1)); else - lua_pushfstring(L, "dentry '%s'", lua_tostring(L, -1)); + lua_pushfstring(L, "dentry: '%s'", lua_tostring(L, -1)); return 1; } @@ -221,7 +221,7 @@ static int meth_inode_tostring(lua_State *L) { struct inode *inode = toinode(L, 1); char buffer[128]; - int l = snprintf(buffer, sizeof(buffer), "inode [%lu]", inode->i_ino); + int l = snprintf(buffer, sizeof(buffer), "inode: [%lu]", inode->i_ino); lua_pushlstring(L, buffer, l); return 1; } @@ -320,9 +320,9 @@ static int meth_file_tostring(lua_State *L) struct file *file = tofile(L, 1); int nres = aux_file_path(L, file); if (nres == 2) - lua_pushfstring(L, "file ", lua_tostring(L, -1)); + lua_pushfstring(L, "file: ", lua_tostring(L, -1)); else - lua_pushfstring(L, "file '%s'", lua_tostring(L, -1)); + lua_pushfstring(L, "file: '%s'", lua_tostring(L, -1)); return 1; } @@ -398,9 +398,9 @@ static int meth_path_tostring(lua_State *L) struct path *path = topath(L, 1); int nres = aux_dentry_path(L, path->dentry, 0); if (nres == 2) - lua_pushfstring(L, "path ", lua_tostring(L, -1)); + lua_pushfstring(L, "path: ", lua_tostring(L, -1)); else - lua_pushfstring(L, "path '%s'", lua_tostring(L, -1)); + lua_pushfstring(L, "path: '%s'", lua_tostring(L, -1)); return 1; } @@ -444,7 +444,7 @@ static int meth_superblock_tostring(lua_State *L) { struct super_block *sb = tosuperblock(L, 1); struct file_system_type *type = sb->s_type; - lua_pushfstring(L, "superblock '%s'", type ? type->name : ""); + lua_pushfstring(L, "superblock: '%s'", type ? type->name : ""); return 1; } @@ -547,15 +547,15 @@ LUALIB_API int luaopen_fs(lua_State *L) { luaL_newlib(L, fslib); - create_dentry_meta(L, fs_dentry_meth); - create_inode_meta(L, fs_inode_meth); - create_file_meta(L, fs_file_meth); - create_binprm_meta(L, fs_binprm_meth); - create_path_meta(L, fs_path_meth); - create_superblock_meta(L, fs_superblock_meth); - create_fscontext_meta(L, fs_fscontext_meth); - create_vfsmount_meta(L, fs_vfsmount_meth); - create_mntidmap_meta(L, fs_mntidmap_meth); + create_dentry_meta(L, fs_dentry_meth, NULL); + create_inode_meta(L, fs_inode_meth, NULL); + create_file_meta(L, fs_file_meth, NULL); + create_binprm_meta(L, fs_binprm_meth, NULL); + create_path_meta(L, fs_path_meth, NULL); + create_superblock_meta(L, fs_superblock_meth, NULL); + create_fscontext_meta(L, fs_fscontext_meth, NULL); + create_vfsmount_meta(L, fs_vfsmount_meth, NULL); + create_mntidmap_meta(L, fs_mntidmap_meth, NULL); return 1; } diff --git a/security/lua/lua_kernel.c b/security/lua/lua_kernel.c index 08596dfd48b2..61f151fd6e5d 100644 --- a/security/lua/lua_kernel.c +++ b/security/lua/lua_kernel.c @@ -256,7 +256,7 @@ static int kernel_task_cmdline(lua_State *L) static int meth_task_tostring(lua_State *L) { struct task_struct *task = totask(L, 1); - lua_pushfstring(L, "task '%s'", task->comm); + lua_pushfstring(L, "task: '%s'", task->comm); return 1; } @@ -401,12 +401,12 @@ static const luaL_Reg kernellib[] = { LUALIB_API int luaopen_kernel(lua_State *L) { luaL_newlib(L, kernellib); - create_task_meta(L, task_meth); - create_cred_meta(L, cred_meth); - create_perfevent_meta(L, NULL); - create_ipc_meta(L, NULL); - create_msgmsg_meta(L, NULL); - create_key_meta(L, NULL); - create_bdev_meta(L, NULL); + create_task_meta(L, task_meth, NULL); + create_cred_meta(L, cred_meth, NULL); + create_perfevent_meta(L, NULL, NULL); + create_ipc_meta(L, NULL, NULL); + create_msgmsg_meta(L, NULL, NULL); + create_key_meta(L, NULL, NULL); + create_bdev_meta(L, NULL, NULL); return 1; } diff --git a/security/lua/lua_net.c b/security/lua/lua_net.c index 1bb308351334..89b36c1b4e96 100644 --- a/security/lua/lua_net.c +++ b/security/lua/lua_net.c @@ -342,10 +342,10 @@ LUALIB_API int luaopen_net(lua_State *L) { luaL_newlib(L, netlib); - create_sock_meta(L, sock_meth); - create_socket_meta(L, socket_meth); - create_skb_meta(L, skb_meth); - create_sockaddr_meta(L, sockaddr_meth); + create_sock_meta(L, sock_meth, NULL); + create_socket_meta(L, socket_meth, NULL); + create_skb_meta(L, skb_meth, NULL); + create_sockaddr_meta(L, sockaddr_meth, NULL); return 1; } diff --git a/security/lua/lua_object.h b/security/lua/lua_object.h index a466abdd8f8b..285b500f7aa2 100644 --- a/security/lua/lua_object.h +++ b/security/lua/lua_object.h @@ -10,20 +10,22 @@ #include "auxlib.h" -#define METHOD_NAME(class, name) ("method." #class "." #name) +#define METHOD_NAME(name) ("method." #name) +#define METHOD_NAME_RAW(name) ("method." #name ".raw") +#define METHOD_NAME_GC(name) ("method." #name ".gc") -#define LUA_OBJECT_DEFINE(class, name, ctype, d) \ +#define LUA_OBJECT_META_DEFINE(name, ctype, d, metaname) \ static inline ctype *new ## name(lua_State *L) \ { \ ctype *p = (ctype *)lua_newuserdata(L, sizeof(ctype)); \ *p = d; \ - luaL_getmetatable(L, METHOD_NAME(class, name)); \ + luaL_getmetatable(L, metaname); \ lua_setmetatable(L, -2); \ return p; \ } \ static inline ctype *to ## name ## p(lua_State *L, int idx) \ { \ - return (ctype *)luaL_checkudata(L, idx, METHOD_NAME(class, name)); \ + return (ctype *)checkudata3(L, idx, metaname); \ } \ static inline ctype to ## name(lua_State *L, int idx) \ { \ @@ -31,64 +33,73 @@ } \ static inline ctype *checkudata_ ## name(lua_State *L, int idx) \ { \ - return (ctype *)checkudata(L, idx, METHOD_NAME(class, name)); \ - } \ - static int class ## _ ## name ## _tostring(lua_State *L) \ - { \ - ctype o = to ## name(L, 1); \ - lua_pushfstring(L, #name " (%p)", o); \ - return 1; \ + return (ctype *)checkudata(L, idx, metaname); \ } -#define LUA_OBJECT_KVCACHE_FUNC(class, name, ctype, blob, method, fname) \ - static int class ## _ ## name ## _ ## method(lua_State *L) \ +#define LUA_OBJECT_KVCACHE_FUNC(name, ctype, blob, method, fname) \ + static int rawmeth_ ## name ## _ ## method(lua_State *L) \ { \ - ctype p = to ## name(L, 1); \ + ctype p = toraw ## name(L, 1); \ struct lua_lsm_ ## blob *ll = lua_lsm_ ## name(p); \ return lua_object_ ## fname(L, &ll->dict); \ } -#define LUA_OBJECT_BLOB_FUNCS_DEFINE(class, name, ctype, d, blob) \ - LUA_OBJECT_DEFINE(class, name, ctype, d) \ - LUA_OBJECT_KVCACHE_FUNC(class, name, ctype, blob, kvcache_get, get) \ - LUA_OBJECT_KVCACHE_FUNC(class, name, ctype, blob, kvcache_incr, incr) \ - LUA_OBJECT_KVCACHE_FUNC(class, name, ctype, blob, index, index) \ - LUA_OBJECT_KVCACHE_FUNC(class, name, ctype, blob, newindex, newindex) \ +#define LUA_OBJECT_TOSTRING_FUNC(name, ctype) \ + static int rawmeth_ ## name ## _tostring(lua_State *L) \ + { \ + ctype o = toraw ## name(L, 1); \ + lua_pushfstring(L, #name ": <%p>", o); \ + return 1; \ + } + +#define LUA_OBJECT_BLOB_FUNCS_DEFINE(name, ctype, d, blob) \ + LUA_OBJECT_META_DEFINE(name, ctype, d, METHOD_NAME(name)) \ + LUA_OBJECT_META_DEFINE(raw ## name, ctype, d, METHOD_NAME_RAW(name)) \ + LUA_OBJECT_META_DEFINE(gc ## name, ctype, d, METHOD_NAME_GC(name)) \ + LUA_OBJECT_KVCACHE_FUNC(name, ctype, blob, kvcache_get, get) \ + LUA_OBJECT_KVCACHE_FUNC(name, ctype, blob, kvcache_incr, incr) \ + LUA_OBJECT_KVCACHE_FUNC(name, ctype, blob, index, index) \ + LUA_OBJECT_KVCACHE_FUNC(name, ctype, blob, newindex, newindex) \ + LUA_OBJECT_TOSTRING_FUNC(name, ctype) \ static inline void create_ ## name ## _meta(lua_State *L, \ - const luaL_Reg *funcs) \ + const luaL_Reg *funcs, const luaL_Reg *gc) \ { \ - static const luaL_Reg object_meth[] = { \ - { "kvcache_set", class ## _ ## name ## _newindex }, \ - { "kvcache_get", class ## _ ## name ## _kvcache_get }, \ - { "kvcache_incr", class ## _ ## name ## _kvcache_incr }, \ - { "__index", class ## _ ## name ## _index }, \ - { "__newindex", class ## _ ## name ## _newindex }, \ - { "__tostring", class ## _ ## name ## _tostring }, \ + static const luaL_Reg rawmeths[] = { \ + { "kvcache_set", rawmeth_ ## name ## _newindex }, \ + { "kvcache_get", rawmeth_ ## name ## _kvcache_get }, \ + { "kvcache_incr", rawmeth_ ## name ## _kvcache_incr }, \ + { NULL, NULL } \ + }; \ + static const luaL_Reg basemeths[] = { \ + { "__index", rawmeth_ ## name ## _index }, \ + { "__newindex", rawmeth_ ## name ## _newindex }, \ + { "__tostring", rawmeth_ ## name ## _tostring }, \ { NULL, NULL } \ }; \ - createmeta(L, METHOD_NAME(class, name), object_meth, 0, 0); \ - if (funcs) \ - luaL_register(L, NULL, funcs); \ - lua_pop(L, 1); \ + createmeta3(L, #name, basemeths, METHOD_NAME_GC(name), gc, \ + METHOD_NAME(name), funcs, METHOD_NAME_RAW(name), rawmeths); \ } -#define LUA_OBJECT_func_DEFINE(class, name, ctype, d) \ - LUA_OBJECT_DEFINE(class, name, ctype, d) \ +#define LUA_OBJECT_func_DEFINE(name, ctype, d) \ + LUA_OBJECT_META_DEFINE(name, ctype, d, METHOD_NAME(name)) \ + LUA_OBJECT_META_DEFINE(raw ## name, ctype, d, METHOD_NAME_RAW(name)) \ + LUA_OBJECT_META_DEFINE(gc ## name, ctype, d, METHOD_NAME_GC(name)) \ + LUA_OBJECT_TOSTRING_FUNC(name, ctype) \ static inline void create_ ## name ## _meta(lua_State *L, \ - const luaL_Reg *funcs) \ + const luaL_Reg *funcs, const luaL_Reg *gc) \ { \ - createmeta(L, METHOD_NAME(class, name), NULL, 1, 0); \ - lua_pushcfunction(L, class ## _ ## name ## _tostring); \ - lua_setfield(L, -2, "__tostring"); /* mt.__tostring = func */ \ - if (funcs) \ - luaL_register(L, NULL, funcs); \ - lua_pop(L, 1); \ + static const luaL_Reg basemeths[] = { \ + { "__tostring", rawmeth_ ## name ## _tostring }, \ + { NULL, NULL } \ + }; \ + createmeta3(L, #name, basemeths, METHOD_NAME_GC(name), gc, \ + METHOD_NAME(name), funcs, METHOD_NAME_RAW(name), NULL); \ } -#define LUA_OBJECT_task_DEFINE(class, name, ctype, d) \ - LUA_OBJECT_BLOB_FUNCS_DEFINE(class, name, ctype, d, task) -#define LUA_OBJECT_object_DEFINE(class, name, ctype, d) \ - LUA_OBJECT_BLOB_FUNCS_DEFINE(class, name, ctype, d, object) +#define LUA_OBJECT_task_DEFINE(name, ctype, d) \ + LUA_OBJECT_BLOB_FUNCS_DEFINE(name, ctype, d, task) +#define LUA_OBJECT_object_DEFINE(name, ctype, d) \ + LUA_OBJECT_BLOB_FUNCS_DEFINE(name, ctype, d, object) #define LUA_OBJECTS_LIST \ @@ -116,7 +127,7 @@ #define LUA_OBJECT(blob, class, name, ctype, d) \ - LUA_OBJECT_ ## blob ## _DEFINE(class, name, ctype, d) + LUA_OBJECT_ ## blob ## _DEFINE(name, ctype, d) LUA_OBJECTS_LIST #undef LUA_OBJECT From 308d7915b7f7418b0d018be4cf05472957dc4dd7 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Tue, 9 Dec 2025 10:46:24 +0800 Subject: [PATCH 060/110] lua-lsm: Strengthen file object using layered metatables Also performs a full garbage-collection after unload from Lua VM. --- security/lua/lsm.c | 3 +++ security/lua/lsm_defs.c | 6 +++--- security/lua/lua_fs.c | 29 +++++++++++++++++++---------- security/lua/lua_kernel.c | 3 +-- 4 files changed, 26 insertions(+), 15 deletions(-) diff --git a/security/lua/lsm.c b/security/lua/lsm.c index 1b57aecf9788..35d81c8b41fc 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -706,6 +706,9 @@ static int lvm_remove_module(lua_State *L, struct lua_module *module) lua_pushstring(L, module->name); lua_pushnil(L); lua_rawset(L, -3); + + /* performs a full garbage-collection cycle. */ + lua_gc(L, LUA_GCCOLLECT, 0); err = 0; } else { lua_pop(L, 1); diff --git a/security/lua/lsm_defs.c b/security/lua/lsm_defs.c index 8ac988f2056d..d050a66ebe48 100644 --- a/security/lua/lsm_defs.c +++ b/security/lua/lsm_defs.c @@ -1404,7 +1404,7 @@ LUA_LSM_PREPARE_DEFINE1(file_alloc_security, struct file *, file) */ LUA_LSM_INT_DEFINE1(file_alloc_security, struct file *, file) { - *newfile(L) = file; + *newrawfile(L) = file; } /** @@ -1431,7 +1431,7 @@ LUA_LSM_POSTPONE_DEFINE1(file_free_security, struct file *, file) */ LUA_LSM_VOID_DEFINE1(file_free_security, struct file *, file) { - *newfile(L) = file; + *newrawfile(L) = file; } /** @@ -1474,7 +1474,7 @@ LUA_LSM_INT_DEFINE1(mmap_addr, unsigned long, addr) LUA_LSM_INT_DEFINE4(mmap_file, struct file *, file, unsigned long, reqprot, unsigned long, prot, unsigned long, flags) { - *newfile(L) = file; + file ? *newfile(L) = file : lua_pushnil(L); lua_pushnumber(L, (lua_Number)reqprot); lua_pushnumber(L, (lua_Number)prot); lua_pushnumber(L, (lua_Number)flags); diff --git a/security/lua/lua_fs.c b/security/lua/lua_fs.c index 920ae4ba61fe..a271dbe899aa 100644 --- a/security/lua/lua_fs.c +++ b/security/lua/lua_fs.c @@ -239,13 +239,6 @@ static const luaL_Reg fs_inode_meth[] = { /********************************** file **********************************/ -static int fs_file_fput(lua_State *L) -{ - struct file *file = tofile(L, 1); - fput(file); - return 0; -} - static int fs_file_dentry(lua_State *L) { const struct file *file = tofile(L, 1); @@ -326,15 +319,31 @@ static int meth_file_tostring(lua_State *L) return 1; } +static int meth_file_gc(lua_State *L) +{ + struct file **filp = togcfilep(L, 1); + if (*filp) { + fput(*filp); + *filp = NULL; + } + return 0; +} + static const luaL_Reg fs_file_meth[] = { - { "fput", fs_file_fput }, { "dentry", fs_file_dentry }, { "inode", fs_file_inode }, { "xattr", fs_file_xattr }, { "size", fs_file_size }, { "fmode", fs_file_fmode }, { "path", fs_file_path }, + { "fput", meth_file_gc }, + { "__tostring", meth_file_tostring }, + { NULL, NULL } +}; + +static const luaL_Reg fs_file_gc_meth[] = { { "__tostring", meth_file_tostring }, + { "__gc", meth_file_gc }, { NULL, NULL } }; @@ -528,7 +537,7 @@ static int fs_filp_open(lua_State *L) const char *filename = luaL_checkstring(L, 1); int flags = luaL_checkinteger(L, 2); umode_t mode = luaL_checkinteger(L, 3); - struct file **filp = newfile(L); + struct file **filp = newgcfile(L); *filp = filp_open(filename, flags, mode); if (IS_ERR(*filp)) { lua_pushnil(L); @@ -549,7 +558,7 @@ LUALIB_API int luaopen_fs(lua_State *L) create_dentry_meta(L, fs_dentry_meth, NULL); create_inode_meta(L, fs_inode_meth, NULL); - create_file_meta(L, fs_file_meth, NULL); + create_file_meta(L, fs_file_meth, fs_file_gc_meth); create_binprm_meta(L, fs_binprm_meth, NULL); create_path_meta(L, fs_path_meth, NULL); create_superblock_meta(L, fs_superblock_meth, NULL); diff --git a/security/lua/lua_kernel.c b/security/lua/lua_kernel.c index 61f151fd6e5d..77ec86f6a373 100644 --- a/security/lua/lua_kernel.c +++ b/security/lua/lua_kernel.c @@ -219,8 +219,7 @@ static int kernel_task_exe_file(lua_State *L) struct file *exe_file = get_task_exe_file(task); if (exe_file == NULL) return 0; - /* XXX: fput() must be called after use. */ - *newfile(L) = exe_file; + *newgcfile(L) = exe_file; return 1; } From ef40c03fd87f0cfd94af391c0bddfaddd136d21c Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Thu, 11 Dec 2025 17:03:26 +0800 Subject: [PATCH 061/110] lua-lsm: Simplify module register function --- security/lua/lsm.c | 116 ++++++++++++++++++++++++--------------------- 1 file changed, 62 insertions(+), 54 deletions(-) diff --git a/security/lua/lsm.c b/security/lua/lsm.c index 35d81c8b41fc..e10c74818726 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -524,29 +524,14 @@ int lua_module_register(const char *code, size_t len) if (IS_ERR(L)) return PTR_ERR(L); - err = luaL_loadbuffer_wrap(L, code, len, ""); + lua_pushcfunction(L, lua_traceback); + err = luaL_loadbuffer_wrap(L, code, len, ""); if (err) goto err_free_lua; - /* dump function */ - luaL_checktype(L, -1, LUA_TFUNCTION); - luaL_buffinit(L, &B); - status = lua_dump(L, lvm_writer, &B); - if (status != 0) { - __log_err("dump: unable to dump the function\n"); - err = -EFAULT; - goto err_free_lua; - } - luaL_pushresult(&B); - chunk = lua_tolstring(L, -1, &chunk_len); - __log_info("compiled, source_len = %d, chunk_len = %d\n", - (int)len, (int)chunk_len); - /* TODO: run in sandbox, record the `require` lua modules */ - lua_pushcfunction(L, lua_traceback); - lua_pushvalue(L, -3); - /* stack: [func, chunk, traceback, func] */ - err = lua_pcall_wrap(L, 0, LUA_MULTRET, -2); + /* stack: [traceback, func] */ + err = lua_pcall_wrap(L, 0, 1, -2); if (err) goto err_free_lua; if (!lua_istable(L, -1)) { @@ -568,7 +553,20 @@ int lua_module_register(const char *code, size_t len) /* traversal the result table */ lua_pushnil(L); while (lua_next(L, -2) != 0) { + static const struct { + const char *field; + int type; + int offset; + } fields[] = { + { "name", LUA_TSTRING, offsetof(struct lua_module, name) }, + { "author", LUA_TSTRING, offsetof(struct lua_module, author) }, + { "description", LUA_TSTRING, offsetof(struct lua_module, description) }, + { "license", LUA_TSTRING, offsetof(struct lua_module, license) }, + { "version", LUA_TNUMBER, offsetof(struct lua_module, version) }, + { NULL } + }; const char *key, *s; + char *p; if (!lua_isstring(L, -2)) { __log_err("module table index must be a string\n"); @@ -577,48 +575,38 @@ int lua_module_register(const char *code, size_t len) } key = lua_tostring(L, -2); - if (strcmp(key, "name") == 0) { - if (lua_isstring(L, -1)) { - s = lua_tostring(L, -1); - module->name = kstrdup(s, GFP_KERNEL); - } else { - __log_err("'%s' value must be a string\n", key); - } - } else if (strcmp(key, "author") == 0) { - if (lua_isstring(L, -1)) { - s = lua_tostring(L, -1); - module->author = kstrdup(s, GFP_KERNEL); - } else { - __log_err("'%s' value must be a string\n", key); - } - } else if (strcmp(key, "description") == 0) { - if (lua_isstring(L, -1)) { - s = lua_tostring(L, -1); - module->description = kstrdup(s, GFP_KERNEL); - } else { - __log_err("'%s' value must be a string\n", key); + + for (i = 0; fields[i].field; i++) { + if (strcmp(key, fields[i].field) != 0) + continue; + + if (lua_type(L, -1) != fields[i].type) { + __log_err("field '%s' must be a %s\n", + key, lua_typename(L, fields[i].type)); + break; } - } else if (strcmp(key, "license") == 0) { - if (lua_isstring(L, -1)) { + + p = (char *)module + fields[i].offset; + switch (lua_type(L, -1)) { + case LUA_TSTRING: s = lua_tostring(L, -1); - module->license = kstrdup(s, GFP_KERNEL); - } else { - __log_err("'%s' value must be a string\n", key); - } - } else if (strcmp(key, "version") == 0) { - if (lua_isnumber(L, -1)) { - module->version = (int)lua_tointeger(L, -1); - } else { - __log_err("'%s' value must be a integer\n", key); + *(const char **)p = kstrdup(s, GFP_KERNEL); + break; + case LUA_TNUMBER: + *(int *)p = (int)lua_tointeger(L, -1); + break; } - } else { + break; + } + + if (fields[i].field == NULL) { for (i = 0; lua_lsm_hook_stats[i].name; i++) { if (strcmp(lua_lsm_hook_stats[i].name, key) != 0) continue; if (!lua_isfunction(L, -1)) { - __log_err("'%s' field must be a function\n", key); - continue; + __log_err("field '%s' must be a function\n", key); + break; } module->nhooks += 1; @@ -629,7 +617,7 @@ int lua_module_register(const char *code, size_t len) } if (lua_lsm_hook_stats[i].name == NULL) - __log_warn("'%s' is unknown function\n", key); + __log_warn("field '%s' is unknown\n", key); } /* removes 'value'; keeps 'key' for next iteration */ @@ -640,6 +628,26 @@ int lua_module_register(const char *code, size_t len) if (module->name == NULL) goto err_free_module; + /* Recompile with the new name */ + err = luaL_loadbuffer_wrap(L, code, len, module->name); + if (err) + goto err_free_module; + + /* dump function */ + luaL_checktype(L, -1, LUA_TFUNCTION); + luaL_buffinit(L, &B); + status = lua_dump(L, lvm_writer, &B); + if (status != 0) { + __log_err("dump: unable to dump the function\n"); + err = -EFAULT; + goto err_free_module; + } + luaL_pushresult(&B); + /* stack: [traceback, _M, func, chunk] */ + chunk = lua_tolstring(L, -1, &chunk_len); + __log_info("[%s] compiled, source_len = %d, chunk_len = %d\n", + module->name, (int)len, (int)chunk_len); + module->chunk = kmalloc(chunk_len, GFP_KERNEL); if (module->chunk == NULL) goto err_free_module; From e7137b536494c66034d4ee13f26285cc7a97836a Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Thu, 11 Dec 2025 17:39:48 +0800 Subject: [PATCH 062/110] lua-lsm: rename lua_module to lua_lsm_module --- security/lua/kvcache.c | 36 ++++++++++++++++++------------------ security/lua/kvcache.h | 6 +++--- security/lua/lsm.c | 40 ++++++++++++++++++++-------------------- security/lua/lsm.h | 8 ++++---- security/lua/lsm_defs.h | 2 +- 5 files changed, 46 insertions(+), 46 deletions(-) diff --git a/security/lua/kvcache.c b/security/lua/kvcache.c index 5583e2eadef1..f31f9a812c22 100644 --- a/security/lua/kvcache.c +++ b/security/lua/kvcache.c @@ -47,7 +47,7 @@ static int kvcache_result(lua_State *L, int err) } static struct kvcache_node * -kvcache_node_alloc(struct kvcache_dict *dict, struct lua_module *module, +kvcache_node_alloc(struct kvcache_dict *dict, struct lua_lsm_module *module, const char *key, size_t len) { size_t l = sizeof(struct kvcache_node); @@ -108,7 +108,7 @@ static void kvcache_node_drop(struct kvcache_node *node) static struct kvcache_node * kvcache_lookup(struct kvcache_dict *dict, - struct lua_module *module, const char *key) + struct lua_lsm_module *module, const char *key) { struct kvcache_node tmp, *node; unsigned long flags; @@ -123,7 +123,7 @@ kvcache_lookup(struct kvcache_dict *dict, static struct kvcache_node * kvcache_module_link(struct kvcache_dict *dict, - struct lua_module *module, struct kvcache_node *node) + struct lua_lsm_module *module, struct kvcache_node *node) { struct kvcache_node *prev; unsigned long flags; @@ -156,7 +156,7 @@ kvcache_module_link(struct kvcache_dict *dict, static void kvcache_module_unlink_unlocked(struct kvcache_dict *dict, - struct lua_module *module, struct kvcache_node *node) + struct lua_lsm_module *module, struct kvcache_node *node) { RB_REMOVE(kvcache, &dict->root, node); atomic_dec(&dict->count); @@ -170,7 +170,7 @@ kvcache_module_unlink_unlocked(struct kvcache_dict *dict, static void kvcache_module_unlink(struct kvcache_dict *dict, - struct lua_module *module, struct kvcache_node *node) + struct lua_lsm_module *module, struct kvcache_node *node) { unsigned long flags; write_lock_irqsave(&dict->lock, flags); @@ -235,8 +235,8 @@ static int kvcache_node_refill(lua_State *L, int idx, struct kvcache_node *node) return 0; } -static int -kvcache_set(lua_State *L, struct kvcache_dict *dict, struct lua_module *module) +static int kvcache_set(lua_State *L, struct kvcache_dict *dict, + struct lua_lsm_module *module) { size_t len; const char *key = luaL_checklstring(L, 2, &len); @@ -314,8 +314,8 @@ static int kvcache_node_get(lua_State *L, struct kvcache_node *node) return 0; } -static int -kvcache_get(lua_State *L, struct kvcache_dict *dict, struct lua_module *module) +static int kvcache_get(lua_State *L, struct kvcache_dict *dict, + struct lua_lsm_module *module) { const char *key = luaL_checkstring(L, 2); struct kvcache_node *node; @@ -330,8 +330,8 @@ kvcache_get(lua_State *L, struct kvcache_dict *dict, struct lua_module *module) return 1; } -static int -kvcache_incr(lua_State *L, struct kvcache_dict *dict, struct lua_module *module) +static int kvcache_incr(lua_State *L, struct kvcache_dict *dict, + struct lua_lsm_module *module) { size_t len; const char *key = luaL_checklstring(L, 2, &len); @@ -388,7 +388,7 @@ kvcache_incr(lua_State *L, struct kvcache_dict *dict, struct lua_module *module) */ static DEFINE_SPINLOCK(nodes_gc_lock); -int kvcache_module_nodes_gc(struct lua_module *module) +int kvcache_module_nodes_gc(struct lua_lsm_module *module) { struct kvcache_node *node, *tmp; int count, n = 0; @@ -422,7 +422,7 @@ int kvcache_module_nodes_gc(struct lua_module *module) void kvcache_dict_free(struct kvcache_dict *dict) { struct kvcache_node *node, *n; - struct lua_module *module; + struct lua_lsm_module *module; spin_lock_bh(&nodes_gc_lock); RB_FOREACH(node, kvcache, &dict->root) { @@ -463,9 +463,9 @@ void kvcache_status(int *nalloc, int *nfree) const int _module_sentinel; -static inline struct lua_module *module_from_object_fenv(lua_State *L, int idx) +static struct lua_lsm_module *module_from_object_fenv(lua_State *L, int idx) { - struct lua_module *module; + struct lua_lsm_module *module; lua_getfenv(L, 1); lua_pushlightuserdata(L, MODULE_KEY); @@ -492,7 +492,7 @@ static inline struct lua_module *module_from_object_fenv(lua_State *L, int idx) int lua_object_get(lua_State *L, struct kvcache_dict *dict) { - struct lua_module *module; + struct lua_lsm_module *module; module = module_from_object_fenv(L, 1); if (module == NULL) @@ -502,7 +502,7 @@ int lua_object_get(lua_State *L, struct kvcache_dict *dict) int lua_object_incr(lua_State *L, struct kvcache_dict *dict) { - struct lua_module *module; + struct lua_lsm_module *module; module = module_from_object_fenv(L, 1); if (module == NULL) @@ -554,7 +554,7 @@ int lua_object_index(lua_State *L, struct kvcache_dict *dict) */ int lua_object_newindex(lua_State *L, struct kvcache_dict *dict) { - struct lua_module *module; + struct lua_lsm_module *module; module = module_from_object_fenv(L, 1); if (module == NULL) diff --git a/security/lua/kvcache.h b/security/lua/kvcache.h index e29ae4362969..15d7a4c1134f 100644 --- a/security/lua/kvcache.h +++ b/security/lua/kvcache.h @@ -17,11 +17,11 @@ #define CACHE_CAPACITY 1024 -struct lua_module; +struct lua_lsm_module; struct kvcache_node { const char *key; - struct lua_module *module; + struct lua_lsm_module *module; struct kvcache_dict *dict; atomic_t refcount; rwlock_t lock; @@ -42,7 +42,7 @@ struct kvcache_dict { RB_HEAD(kvcache, kvcache_node) root; }; -int kvcache_module_nodes_gc(struct lua_module *module); +int kvcache_module_nodes_gc(struct lua_lsm_module *module); void kvcache_dict_free(struct kvcache_dict *dict); void kvcache_dict_init(struct kvcache_dict *dict); void kvcache_status(int *nalloc, int *nfree); diff --git a/security/lua/lsm.c b/security/lua/lsm.c index e10c74818726..8c758eaa2ae4 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -130,8 +130,8 @@ void lvm_put(lua_State *L) static int lua_shared_index(lua_State *L) { const char *name = luaL_checkstring(L, 2); - struct lua_module_shdict *shdict, *shtmp; - struct lua_module *module; + struct lua_lsm_module_shdict *shdict, *shtmp; + struct lua_lsm_module *module; int found = 0; __log_info_ratelimited("READ shared table, [%s] %s\n", @@ -218,7 +218,7 @@ static int lua_module_fenv_newindex(lua_State *L) return 0; } -static int lua_module_load(lua_State *L, struct lua_module *module) +static int lua_module_load(lua_State *L, struct lua_lsm_module *module) { int err; @@ -285,7 +285,7 @@ static int lua_module_load(lua_State *L, struct lua_module *module) static int lua_module_index(lua_State *L) { const char *key = luaL_checkstring(L, 2); - struct lua_module *module; + struct lua_lsm_module *module; int err; /* module queries are always run with a read lock */ @@ -320,7 +320,7 @@ static int lua_module_index(lua_State *L) */ static void lua_modules_free(struct task_struct *task, lua_State *L) { - struct lua_module *module; + struct lua_lsm_module *module; lua_getfield(L, LUA_REGISTRYINDEX, "_MODULES"); if (!lua_istable(L, -1)) { @@ -498,7 +498,7 @@ static int lvm_writer(lua_State *L, const void *b, size_t size, void *B) return 0; } -static void lua_module_free(struct lua_module *module) +static void lua_module_free(struct lua_lsm_module *module) { kfree(module->chunk); kfree(module->name); @@ -510,7 +510,7 @@ static void lua_module_free(struct lua_module *module) int lua_module_register(const char *code, size_t len) { - struct lua_module *module, *m; + struct lua_lsm_module *module, *m; lua_State *L; luaL_Buffer B; const char *chunk; @@ -558,11 +558,11 @@ int lua_module_register(const char *code, size_t len) int type; int offset; } fields[] = { - { "name", LUA_TSTRING, offsetof(struct lua_module, name) }, - { "author", LUA_TSTRING, offsetof(struct lua_module, author) }, - { "description", LUA_TSTRING, offsetof(struct lua_module, description) }, - { "license", LUA_TSTRING, offsetof(struct lua_module, license) }, - { "version", LUA_TNUMBER, offsetof(struct lua_module, version) }, + { "name", LUA_TSTRING, offsetof(struct lua_lsm_module, name) }, + { "author", LUA_TSTRING, offsetof(struct lua_lsm_module, author) }, + { "description", LUA_TSTRING, offsetof(struct lua_lsm_module, description) }, + { "license", LUA_TSTRING, offsetof(struct lua_lsm_module, license) }, + { "version", LUA_TNUMBER, offsetof(struct lua_lsm_module, version) }, { NULL } }; const char *key, *s; @@ -700,7 +700,7 @@ int lua_module_register(const char *code, size_t len) return err; } -static int lvm_remove_module(lua_State *L, struct lua_module *module) +static int lvm_remove_module(lua_State *L, struct lua_lsm_module *module) { int err = -ENOENT; /* registry._MODULES[modname] = nil */ @@ -728,7 +728,7 @@ static int lvm_remove_module(lua_State *L, struct lua_module *module) static int task_remove_module(struct task_struct *task, void *arg) { - struct lua_module *module = arg; + struct lua_lsm_module *module = arg; lua_State *L; int err; @@ -744,7 +744,7 @@ static int task_remove_module(struct task_struct *task, void *arg) return err; } -static int tasks_lvm_remove_module(struct lua_module *module, int *nbusy) +static int tasks_lvm_remove_module(struct lua_lsm_module *module, int *nbusy) { struct task_struct *g, *task; int count = 0; @@ -782,12 +782,12 @@ static int tasks_lvm_remove_module(struct lua_module *module, int *nbusy) * Due to the limitations of schedule_on_each_cpu(), global variables * are used to pass parameters to the callback function. */ -static struct lua_module *work_ctx_remove_module; +static struct lua_lsm_module *work_ctx_remove_module; static atomic_t work_ctx_remove_count; static void softirq_lvm_remove_module(struct work_struct *work) { - struct lua_module *module = work_ctx_remove_module; + struct lua_lsm_module *module = work_ctx_remove_module; int cpu = smp_processor_id(); int err; @@ -808,8 +808,8 @@ static void softirq_lvm_remove_module(struct work_struct *work) int lua_module_unregister(const char *name) { - struct lua_module *module; - struct lua_module_shdict *shdict, *tmp; + struct lua_lsm_module *module; + struct lua_lsm_module_shdict *shdict, *tmp; int count = 0, nloaded, nbusy; unsigned int cpu; int found = 0; @@ -937,7 +937,7 @@ int lua_module_unregister(const char *name) int modules_show(struct seq_file *m, void *v) { - struct lua_module *module; + struct lua_lsm_module *module; int idx; seq_printf(m, "modules for lua-lsm\n"); diff --git a/security/lua/lsm.h b/security/lua/lsm.h index 9bf4438acbb1..d39670b59e3b 100644 --- a/security/lua/lsm.h +++ b/security/lua/lsm.h @@ -53,26 +53,26 @@ enum { __LL_NR_MAX }; -struct lua_module_shdict { +struct lua_lsm_module_shdict { struct list_head list; struct kvcache_dict dict; char name[]; }; -enum lua_module_state { +enum lua_lsm_module_state { LMS_STATE_LIVE, LMS_STATE_COMING, LMS_STATE_GOING, LMS_STATE_ZOMBIE, }; -struct lua_module { +struct lua_lsm_module { const char *name; const char *author; const char *description; const char *license; int version; - enum lua_module_state state; + enum lua_lsm_module_state state; struct list_head list; __BITMAP_TYPE(, uint32_t, __LL_NR_MAX) hookfuncs; int nhooks; diff --git a/security/lua/lsm_defs.h b/security/lua/lsm_defs.h index e94c562e386d..e1ea27d651fd 100644 --- a/security/lua/lsm_defs.h +++ b/security/lua/lsm_defs.h @@ -122,7 +122,7 @@ static inline int __lua_lsm_ ## NAME(DECL_ARGS_ ## x \ __MAP(x, __SC_DECL, __VA_ARGS__)) \ { \ - struct lua_module *module; \ + struct lua_lsm_module *module; \ lua_State *L; \ int ret = LSM_RET_DEFAULT(NAME); \ if (atomic_read(&lua_lsm_hook_stats[__LL_NR_ ## NAME].nhooks) == 0) \ From d65fe9aa54e7a1723b5e9db3de785a15d9d42f8c Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Thu, 11 Dec 2025 20:05:51 +0800 Subject: [PATCH 063/110] lua-lsm: Fixes for object missing environment --- security/lua/lua_capability.c | 7 +++++++ security/lua/lua_fs.c | 12 ++++++------ security/lua/lua_kernel.c | 6 ++++++ security/lua/lua_net.c | 6 ++++++ 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/security/lua/lua_capability.c b/security/lua/lua_capability.c index 0fe15c8268fa..03a2e87346f1 100644 --- a/security/lua/lua_capability.c +++ b/security/lua/lua_capability.c @@ -65,11 +65,13 @@ static int capability_add(lua_State *L) if (tt == LUA_TUSERDATA) { const kernel_cap_t cap2 = tocap(L, 2); *newcap(L) = cap_combine(cap1, cap2); + settopfenvfrom(L, 1); return 1; } else if (tt == LUA_TNUMBER) { int flag = luaL_checkint(L, 2); cap_raise(cap1, flag); *newcap(L) = cap1; + settopfenvfrom(L, 1); return 1; } else { return 0; @@ -83,11 +85,13 @@ static int capability_sub(lua_State *L) if (tt == LUA_TUSERDATA) { const kernel_cap_t cap2 = tocap(L, 2); *newcap(L) = cap_drop(cap1, cap2); + settopfenvfrom(L, 1); return 1; } else if (tt == LUA_TNUMBER) { int flag = luaL_checkint(L, 2); cap_lower(cap1, flag); *newcap(L) = cap1; + settopfenvfrom(L, 1); return 1; } else { return 0; @@ -99,6 +103,7 @@ static int capability_mul(lua_State *L) const kernel_cap_t cap1 = tocap(L, 1); const kernel_cap_t cap2 = tocap(L, 2); *newcap(L) = cap_intersect(cap1, cap2); + settopfenvfrom(L, 1); return 1; } @@ -142,12 +147,14 @@ static const luaL_Reg cap_meth[] = { static int capability_cap_empty(lua_State *L) { *newcap(L) = CAP_EMPTY_SET; + /* TODO: settopfenvfrom(L, 1); */ return 1; } static int capability_cap_full(lua_State *L) { *newcap(L) = CAP_FULL_SET; + /* TODO: settopfenvfrom(L, 1); */ return 1; } diff --git a/security/lua/lua_fs.c b/security/lua/lua_fs.c index a271dbe899aa..63ccf287e1ff 100644 --- a/security/lua/lua_fs.c +++ b/security/lua/lua_fs.c @@ -66,8 +66,7 @@ static int fs_dentry_dput(lua_State *L) static int fs_dentry_backing_inode(lua_State *L) { struct dentry *dentry = todentry(L, 1); - struct inode **inodep = newinode(L); - *inodep = d_backing_inode(dentry); + *newinode(L) = d_backing_inode(dentry); settopfenvfrom(L, 1); return 1; } @@ -242,8 +241,7 @@ static const luaL_Reg fs_inode_meth[] = { static int fs_file_dentry(lua_State *L) { const struct file *file = tofile(L, 1); - struct dentry **dentryp = newdentry(L); - *dentryp = file_dentry(file); + *newdentry(L) = file_dentry(file); settopfenvfrom(L, 1); return 1; } @@ -251,8 +249,7 @@ static int fs_file_dentry(lua_State *L) static int fs_file_inode(lua_State *L) { struct file *file = tofile(L, 1); - struct inode **inodep = newinode(L); - *inodep = file_inode(file); + *newinode(L) = file_inode(file); settopfenvfrom(L, 1); return 1; } @@ -489,6 +486,7 @@ static int fs_vfsmount_superblock(lua_State *L) { struct vfsmount *mnt = tovfsmount(L, 1); *newsuperblock(L) = mnt->mnt_sb; + settopfenvfrom(L, 1); return 1; } @@ -496,6 +494,7 @@ static int fs_vfsmount_mntidmap(lua_State *L) { struct vfsmount *mnt = tovfsmount(L, 1); *newmntidmap(L) = mnt_idmap(mnt); + settopfenvfrom(L, 1); return 1; } @@ -544,6 +543,7 @@ static int fs_filp_open(lua_State *L) lua_pushinteger(L, PTR_ERR(*filp)); return 2; } + /* TODO: settopfenvfrom(L, 1); */ return 1; } diff --git a/security/lua/lua_kernel.c b/security/lua/lua_kernel.c index 77ec86f6a373..1dd8da69655f 100644 --- a/security/lua/lua_kernel.c +++ b/security/lua/lua_kernel.c @@ -52,8 +52,11 @@ static int kernel_cred_cap_eip(lua_State *L) if (top == 1) { /* get caps */ *newcap(L) = cred->cap_effective; + settopfenvfrom(L, 1); *newcap(L) = cred->cap_inheritable; + settopfenvfrom(L, 1); *newcap(L) = cred->cap_permitted; + settopfenvfrom(L, 1); return 3; } /* set caps */ @@ -72,6 +75,7 @@ static int kernel_cred_cap_bset(lua_State *L) struct cred *cred = tocred(L, 1); if (lua_gettop(L) == 1) { *newcap(L) = cred->cap_bset; + settopfenvfrom(L, 1); return 1; } else { cred->cap_bset = tocap(L, 2); @@ -85,6 +89,7 @@ static int kernel_cred_cap_ambient(lua_State *L) struct cred *cred = tocred(L, 1); if (lua_gettop(L) == 1) { *newcap(L) = cred->cap_ambient; + settopfenvfrom(L, 1); return 1; } else { cred->cap_ambient = tocap(L, 2); @@ -220,6 +225,7 @@ static int kernel_task_exe_file(lua_State *L) if (exe_file == NULL) return 0; *newgcfile(L) = exe_file; + settopfenvfrom(L, 1); return 1; } diff --git a/security/lua/lua_net.c b/security/lua/lua_net.c index 89b36c1b4e96..89a6f6a6a56a 100644 --- a/security/lua/lua_net.c +++ b/security/lua/lua_net.c @@ -48,6 +48,7 @@ static int net_sock_socket(lua_State *L) { struct sock *sk = tosock(L, 1); *newsocket(L) = sk->sk_socket; + settopfenvfrom(L, 1); return 1; } @@ -158,6 +159,7 @@ static int net_socket_sock(lua_State *L) { struct socket *sock = tosocket(L, 1); *newsock(L) = sock->sk; + settopfenvfrom(L, 1); return 1; } @@ -165,6 +167,7 @@ static int net_socket_inode(lua_State *L) { struct socket *sock = tosocket(L, 1); *newinode(L) = SOCK_INODE(sock); + settopfenvfrom(L, 1); return 1; } @@ -181,6 +184,7 @@ static int net_skb_sock(lua_State *L) { struct sk_buff *skb = toskb(L, 1); *newsock(L) = skb->sk; + settopfenvfrom(L, 1); return 1; } @@ -188,6 +192,7 @@ static int net_skb_full_sk(lua_State *L) { struct sk_buff *skb = toskb(L, 1); *newsock(L) = skb_to_full_sk(skb); + settopfenvfrom(L, 1); return 1; } @@ -278,6 +283,7 @@ static int net_sock_alloc(lua_State *L) *sockp = sock_alloc(); if (*sockp == NULL) return 0; + /* TODO: settopfenvfrom(L, 1); */ return 1; } From 166b195cdf16d8a1aa2c9f99b843a29200990edb Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Fri, 12 Dec 2025 14:23:26 +0800 Subject: [PATCH 064/110] lua-lsm: api - make sockaddr readable --- security/lua/lua_net.c | 45 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/security/lua/lua_net.c b/security/lua/lua_net.c index 89a6f6a6a56a..7e479f2dcf2a 100644 --- a/security/lua/lua_net.c +++ b/security/lua/lua_net.c @@ -249,17 +249,30 @@ static int sockaddr_family(lua_State *L) static int sockaddr_addrs(lua_State *L) { struct sockaddr *sa = tosockaddr(L, 1); + int readable = lua_toboolean(L, 2); + char buffer[128]; + int l; switch (sa->sa_family) { case AF_INET: lua_pushstring(L, "inet"); + if (readable) { + l = snprintf(buffer, sizeof(buffer), "%pISc", sa); + lua_pushlstring(L, buffer, l); + } else { + lua_pushinteger(L, ((struct sockaddr_in *)sa)->sin_addr.s_addr); + } lua_pushinteger(L, ntohs(((struct sockaddr_in *)sa)->sin_port)); - lua_pushinteger(L, ((struct sockaddr_in *)sa)->sin_addr.s_addr); return 3; case AF_INET6: lua_pushstring(L, "inet6"); - lua_pushinteger(L, ntohs(((struct sockaddr_in6 *)sa)->sin6_port)); - lua_pushlstring(L, ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr, + if (readable) { + l = snprintf(buffer, sizeof(buffer), "%pISc", sa); + lua_pushlstring(L, buffer, l); + } else { + lua_pushlstring(L, ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr, sizeof(((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr)); + } + lua_pushinteger(L, ntohs(((struct sockaddr_in6 *)sa)->sin6_port)); return 3; case AF_UNIX: lua_pushstring(L, "unix"); @@ -269,9 +282,31 @@ static int sockaddr_addrs(lua_State *L) return 0; } +static int meth_sockaddr_tostring(lua_State *L) +{ + struct sockaddr *sa = tosockaddr(L, 1); + char buffer[128]; + int l; + switch (sa->sa_family) { + case AF_INET: + l = snprintf(buffer, sizeof(buffer), "inet: %pISpc", sa); + lua_pushlstring(L, buffer, l); + break; + case AF_INET6: + l = snprintf(buffer, sizeof(buffer), "inet6: %pISpc", sa); + lua_pushlstring(L, buffer, l); + break; + case AF_UNIX: + lua_pushfstring(L, "unix: %s", ((struct sockaddr_un *)sa)->sun_path); + break; + } + return 1; +} + static const luaL_Reg sockaddr_meth[] = { - { "family", sockaddr_family }, - { "addrs", sockaddr_addrs }, + { "family", sockaddr_family }, + { "addrs", sockaddr_addrs }, + { "__tostring", meth_sockaddr_tostring }, { NULL, NULL } }; From 91ccd2ce845615456c4948a084b9ae53fdf8d6e0 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Fri, 12 Dec 2025 15:12:23 +0800 Subject: [PATCH 065/110] lua-lsm: Add type() method for all userdata --- security/lua/auxlib.c | 5 ----- security/lua/lua_object.h | 32 +++++++++++++++++++++----------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/security/lua/auxlib.c b/security/lua/auxlib.c index 5a645805a6a3..b1947ba633d1 100644 --- a/security/lua/auxlib.c +++ b/security/lua/auxlib.c @@ -302,11 +302,6 @@ void createmeta(lua_State *L, const char *tname, const char *name, /* metatable.__metatable = error_message */ lua_pushstring(L, "cannot set a protected metatable"); lua_setfield(L, -2, "__metatable"); - if (name) { - /* metatable.__name = name */ - lua_pushstring(L, name); - lua_setfield(L, -2, "__name"); - } /* metatable.__index = metatable */ lua_pushvalue(L, -1); diff --git a/security/lua/lua_object.h b/security/lua/lua_object.h index 285b500f7aa2..4bcff36af61a 100644 --- a/security/lua/lua_object.h +++ b/security/lua/lua_object.h @@ -44,7 +44,12 @@ return lua_object_ ## fname(L, &ll->dict); \ } -#define LUA_OBJECT_TOSTRING_FUNC(name, ctype) \ +#define LUA_OBJECT_FUNCS_DEFINE(name, ctype) \ + static int rawmeth_ ## name ## _type(lua_State *L) \ + { \ + lua_pushstring(L, #name); \ + return 1; \ + } \ static int rawmeth_ ## name ## _tostring(lua_State *L) \ { \ ctype o = toraw ## name(L, 1); \ @@ -60,22 +65,23 @@ LUA_OBJECT_KVCACHE_FUNC(name, ctype, blob, kvcache_incr, incr) \ LUA_OBJECT_KVCACHE_FUNC(name, ctype, blob, index, index) \ LUA_OBJECT_KVCACHE_FUNC(name, ctype, blob, newindex, newindex) \ - LUA_OBJECT_TOSTRING_FUNC(name, ctype) \ + LUA_OBJECT_FUNCS_DEFINE(name, ctype) \ static inline void create_ ## name ## _meta(lua_State *L, \ const luaL_Reg *funcs, const luaL_Reg *gc) \ { \ - static const luaL_Reg rawmeths[] = { \ - { "kvcache_set", rawmeth_ ## name ## _newindex }, \ - { "kvcache_get", rawmeth_ ## name ## _kvcache_get }, \ - { "kvcache_incr", rawmeth_ ## name ## _kvcache_incr }, \ - { NULL, NULL } \ - }; \ static const luaL_Reg basemeths[] = { \ { "__index", rawmeth_ ## name ## _index }, \ { "__newindex", rawmeth_ ## name ## _newindex }, \ { "__tostring", rawmeth_ ## name ## _tostring }, \ { NULL, NULL } \ }; \ + static const luaL_Reg rawmeths[] = { \ + { "kvcache_set", rawmeth_ ## name ## _newindex }, \ + { "kvcache_get", rawmeth_ ## name ## _kvcache_get }, \ + { "kvcache_incr", rawmeth_ ## name ## _kvcache_incr }, \ + { "type", rawmeth_ ## name ## _type }, \ + { NULL, NULL } \ + }; \ createmeta3(L, #name, basemeths, METHOD_NAME_GC(name), gc, \ METHOD_NAME(name), funcs, METHOD_NAME_RAW(name), rawmeths); \ } @@ -84,16 +90,20 @@ LUA_OBJECT_META_DEFINE(name, ctype, d, METHOD_NAME(name)) \ LUA_OBJECT_META_DEFINE(raw ## name, ctype, d, METHOD_NAME_RAW(name)) \ LUA_OBJECT_META_DEFINE(gc ## name, ctype, d, METHOD_NAME_GC(name)) \ - LUA_OBJECT_TOSTRING_FUNC(name, ctype) \ + LUA_OBJECT_FUNCS_DEFINE(name, ctype) \ static inline void create_ ## name ## _meta(lua_State *L, \ const luaL_Reg *funcs, const luaL_Reg *gc) \ { \ static const luaL_Reg basemeths[] = { \ - { "__tostring", rawmeth_ ## name ## _tostring }, \ + { "__tostring", rawmeth_ ## name ## _tostring }, \ + { NULL, NULL } \ + }; \ + static const luaL_Reg rawmeths[] = { \ + { "type", rawmeth_ ## name ## _type }, \ { NULL, NULL } \ }; \ createmeta3(L, #name, basemeths, METHOD_NAME_GC(name), gc, \ - METHOD_NAME(name), funcs, METHOD_NAME_RAW(name), NULL); \ + METHOD_NAME(name), funcs, METHOD_NAME_RAW(name), rawmeths); \ } #define LUA_OBJECT_task_DEFINE(name, ctype, d) \ From 6f63f2ff27fdd4c0ca9d3fa140a38b131a89b45d Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Mon, 15 Dec 2025 19:47:06 +0800 Subject: [PATCH 066/110] lua-lsm: Rewrite statistics codes --- security/lua/Kconfig | 2 +- security/lua/kvcache.c | 42 +++-- security/lua/kvcache.h | 6 +- security/lua/lsm.c | 326 ++++++++++++++++++++------------------ security/lua/lsm.h | 19 ++- security/lua/lsm_defs.h | 2 +- security/lua/securityfs.c | 23 ++- 7 files changed, 244 insertions(+), 176 deletions(-) diff --git a/security/lua/Kconfig b/security/lua/Kconfig index 87192cad3a07..1df27b3fc643 100644 --- a/security/lua/Kconfig +++ b/security/lua/Kconfig @@ -9,7 +9,7 @@ config SECURITY_LUA_LSM This selects the Lua based LSM. If you are unsure how to answer this question, answer N. -config SECURITY_LUA_LSM_STATISTICS +config SECURITY_LUA_LSM_STATS bool "Lua-LSM Statistics" depends on SECURITY_LUA_LSM default n diff --git a/security/lua/kvcache.c b/security/lua/kvcache.c index f31f9a812c22..a621446c1f5f 100644 --- a/security/lua/kvcache.c +++ b/security/lua/kvcache.c @@ -16,8 +16,38 @@ #include "lsm.h" #include "kvcache.h" + +#ifdef CONFIG_SECURITY_LUA_LSM_STATS + static atomic_t node_nalloc = ATOMIC_INIT(0); static atomic_t node_nfree = ATOMIC_INIT(0); +static atomic_t node_nusage = ATOMIC_INIT(0); + +static void kvcache_stats_alloc(void) +{ + atomic_inc(&node_nalloc); + atomic_inc(&node_nusage); +} + +static void kvcache_stats_free(void) +{ + atomic_inc(&node_nfree); + atomic_dec(&node_nusage); +} + +void kvcache_stats_show(struct seq_file *m) +{ + seq_printf(m, "kvcache.nalloc\t= %9d\n", atomic_read(&node_nalloc)); + seq_printf(m, "kvcache.nfree\t= %9d\n", atomic_read(&node_nfree)); + seq_printf(m, "kvcache.nusage\t= %9d\n", atomic_read(&node_nusage)); +} + +#else + +static inline void kvcache_stats_alloc(void) {} +static inline void kvcache_stats_free(void) {} + +#endif static int kvcache_node_cmp(struct kvcache_node *n1, struct kvcache_node *n2) { @@ -70,7 +100,7 @@ kvcache_node_alloc(struct kvcache_dict *dict, struct lua_lsm_module *module, node->dict = dict; refcount_init(&node->refcount, 1); rwlock_init(&node->lock); - atomic_inc(&node_nalloc); + kvcache_stats_alloc(); return node; } @@ -88,7 +118,7 @@ static void kvcache_node_clear(struct kvcache_node *node) static void kvcache_node_free(struct kvcache_node *node) { kvcache_node_clear(node); - atomic_inc(&node_nfree); + kvcache_stats_free(); kfree(node); } @@ -451,14 +481,6 @@ void kvcache_dict_init(struct kvcache_dict *dict) dict->capacity = CACHE_CAPACITY; } -void kvcache_status(int *nalloc, int *nfree) -{ - if (nalloc) - *nalloc = atomic_read(&node_nalloc); - if (nfree) - *nfree = atomic_read(&node_nfree); -} - /******************************** object cache *******************************/ const int _module_sentinel; diff --git a/security/lua/kvcache.h b/security/lua/kvcache.h index 15d7a4c1134f..85899905b2cb 100644 --- a/security/lua/kvcache.h +++ b/security/lua/kvcache.h @@ -10,6 +10,7 @@ #include #include +#include #include #include "refcount.h" #undef RB_ROOT @@ -42,10 +43,13 @@ struct kvcache_dict { RB_HEAD(kvcache, kvcache_node) root; }; +#ifdef CONFIG_SECURITY_LUA_LSM_STATS +void kvcache_stats_show(struct seq_file *m); +#endif + int kvcache_module_nodes_gc(struct lua_lsm_module *module); void kvcache_dict_free(struct kvcache_dict *dict); void kvcache_dict_init(struct kvcache_dict *dict); -void kvcache_status(int *nalloc, int *nfree); /******************************** object cache *******************************/ diff --git a/security/lua/lsm.c b/security/lua/lsm.c index 8c758eaa2ae4..a49c878a527b 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -72,40 +72,157 @@ struct lua_lsm_hook_stat lua_lsm_hook_stats[] = { #include #undef LSM_HOOK -/********************************** Lua VM **********************************/ +/********************************** stats **********************************/ -struct lvm_userdata { - bool softirq; -}; +/* used by module unregister */ +static atomic_t vm_nusage = ATOMIC_INIT(0); -static struct lvm_userdata irq_lvms_ud = { - .softirq = true, -}; +#ifdef CONFIG_SECURITY_LUA_LSM_STATS + +static atomic_t vm_nalloc = ATOMIC_INIT(0); +static atomic_t vm_nfree = ATOMIC_INIT(0); +static atomic_t mem_nalloc = ATOMIC_INIT(0); +static atomic_t mem_nrealloc = ATOMIC_INIT(0); +static atomic_t mem_nfree = ATOMIC_INIT(0); +static atomic_t mem_total = ATOMIC_INIT(0); +static atomic_t mem_minimum = ATOMIC_INIT(INT_MAX); +static atomic_t mem_maximum = ATOMIC_INIT(0); + +static void lvm_stats_vmalloc(void) +{ + atomic_inc(&vm_nalloc); + atomic_inc(&vm_nusage); +} + +static void lvm_stats_vmfree(void) +{ + atomic_inc(&vm_nfree); + atomic_dec(&vm_nusage); +} + +static void lvm_stats_memalloc(struct lvm_state *lvm, void *ptr, + size_t osize, size_t nsize) +{ + lua_State *L = lvm->L; + int minimum, maximum, nbytes; + + if (nsize == 0) { + atomic_inc(&mem_nfree); + atomic_inc(&lvm->nfree); + atomic_sub((int)osize, &mem_total); + } else { + if (ptr) { + atomic_inc(&mem_nrealloc); + atomic_inc(&lvm->nrealloc); + } else { + atomic_inc(&mem_nalloc); + atomic_inc(&lvm->nalloc); + } + + atomic_add(nsize - osize, &mem_total); + } + + if (L == NULL) + return; + + minimum = atomic_read(&mem_minimum); + maximum = atomic_read(&mem_maximum); + /* + * XXX Notes: There may be a deadlock risk if internal lua_lock() + * is actually used. The current architecture does not use locking, + * so lua_gc() is fine here. + */ + nbytes = lua_gc(L, LUA_GCCOUNT, 0) * 1024 + lua_gc(L, LUA_GCCOUNTB, 0); + atomic_cmpxchg(&mem_minimum, minimum, min(minimum, nbytes)); + atomic_cmpxchg(&mem_maximum, maximum, max(maximum, nbytes)); +} + +void lvm_stats_show(struct seq_file *m) +{ + int nusage = atomic_read(&vm_nusage); + int total = atomic_read(&mem_total); + int average = nusage ? total / nusage : 0; + + seq_printf(m, "lvm.nalloc\t= %9d\n", atomic_read(&vm_nalloc)); + seq_printf(m, "lvm.nfree\t= %9d\n", atomic_read(&vm_nfree)); + seq_printf(m, "lvm.nusage\t= %9d\n", nusage); + seq_printf(m, "lmem.nalloc\t= %9d\n", atomic_read(&mem_nalloc)); + seq_printf(m, "lmem.nrealloc\t= %9d\n", atomic_read(&mem_nrealloc)); + seq_printf(m, "lmem.nfree\t= %9d\n", atomic_read(&mem_nfree)); + seq_printf(m, "lmem.total\t= %9d\n", total); + seq_printf(m, "lmem.average\t= %9d\n", average); + seq_printf(m, "lmem.minimum\t= %9d\n", atomic_read(&mem_minimum)); + seq_printf(m, "lmem.maximum\t= %9d\n", atomic_read(&mem_maximum)); +} + +int lsm_funcs_show(struct seq_file *m, void *v) +{ + struct lua_lsm_hook_stat *stat; + int i = 1; + + seq_printf(m, "stats for lua-lsm (ns)\n"); + seq_printf(m, "%3s %-28s %4s %12s %15s %10s %12s\n", + "num", "name", "nlsm", "count", "total", "average", "maxtime"); + seq_printf(m, "%s\n", TABLINE); + + for (stat = lua_lsm_hook_stats; stat->name; stat++) { + int n = atomic_read(&stat->count); + s64 total = atomic64_read(&stat->time); + s64 maxtime = atomic64_read(&stat->maxtime); + seq_printf(m, "%3d %-28s %4d %12d %15llu %10llu %12llu\n", + i++, stat->name, atomic_read(&stat->nhooks), + n, total, n ? total / n : 0, maxtime); + } + return 0; +} + +#else + +static inline void lvm_stats_vmalloc(void) +{ + atomic_inc(&vm_nusage); +} + +static inline void lvm_stats_vmfree(void) +{ + atomic_dec(&vm_nusage); +} + +static inline void lvm_stats_memalloc(struct lvm_state *lvm, void *ptr, + size_t osize, size_t nsize) +{ +} + +#endif -static DEFINE_PER_CPU(lua_State *, irq_lvms); +/********************************** Lua VM **********************************/ + +static DEFINE_PER_CPU(struct lvm_state *, irq_lvms); static lua_State * lvm_get_from_task(const struct task_struct *task, bool exclusive) { struct lua_lsm_task *llt = lua_lsm_task(task); - int n = refcount_acquire(&llt->refcount); + struct lvm_state *lvm = &llt->lvm; + int n = refcount_acquire(&lvm->refcount); if (exclusive && n != 1) { - refcount_release(&llt->refcount); + refcount_release(&lvm->refcount); return NULL; } WARN_ON(n != 1); KASSERT(n == 1, ("<%s> Lua VM is reused, refcount = %d\n", task->comm, n)); - return llt->L; + return lvm->L; } static void lvm_put_to_task(const struct task_struct *task, lua_State *L) { struct lua_lsm_task *llt = lua_lsm_task(task); - int n = refcount_release(&llt->refcount); + struct lvm_state *lvm = &llt->lvm; + int n = refcount_release(&lvm->refcount); KASSERT(n == 0, ("<%s> Lua VM is reused, refcount = %d\n", task->comm, n)); - WARN_ON(L != llt->L); + WARN_ON(L != lvm->L); } lua_State *lvm_get(void) @@ -115,7 +232,7 @@ lua_State *lvm_get(void) if (in_task()) return lvm_get_from_task(current, false); else - return get_cpu_var(irq_lvms); + return get_cpu_var(irq_lvms)->L; } void lvm_put(lua_State *L) @@ -343,6 +460,7 @@ static void lua_modules_free(struct task_struct *task, lua_State *L) lua_pop(L, 1); } +/********************************** Lua VM **********************************/ static const luaL_Reg builtinlibs[] = { { "kernel", luaopen_kernel }, @@ -373,31 +491,14 @@ static int ll_require(lua_State *L) return 1; } - -static atomic_t vm_nalloc = ATOMIC_INIT(0); -static atomic_t vm_nfree = ATOMIC_INIT(0); -static atomic_t vm_inuse = ATOMIC_INIT(0); -static atomic_t mem_nalloc = ATOMIC_INIT(0); -static atomic_t mem_nrealloc = ATOMIC_INIT(0); -static atomic_t mem_nfree = ATOMIC_INIT(0); - static void *lvm_alloc(void *ud, void *ptr, size_t osize, size_t nsize) { - struct lvm_userdata *args = ud; - - (void)osize; - (void)args; + lvm_stats_memalloc(ud, ptr, osize, nsize); if (nsize == 0) { - atomic_inc(&mem_nfree); kfree(ptr); return NULL; } else { - if (ptr) - atomic_inc(&mem_nrealloc); - else - atomic_inc(&mem_nalloc); - return krealloc(ptr, nsize, lua_lsm_gfp()); } } @@ -443,15 +544,16 @@ static int lvm_pmain(lua_State *L) return 1; } -static lua_State *lua_state_alloc(struct lvm_userdata *args) +static int lua_state_alloc(struct lvm_state *lvm) { lua_State *L; int status; - L = lua_newstate(lvm_alloc, args); + L = lua_newstate(lvm_alloc, lvm); if (L == NULL) - return ERR_PTR(-ENOMEM); + return -ENOMEM; + lvm->L = L; lua_atpanic(L, lvm_panic); lua_gc(L, LUA_GCSTOP, 0); @@ -472,20 +574,20 @@ static lua_State *lua_state_alloc(struct lvm_userdata *args) if (status != 0) { lua_close(L); - return ERR_PTR(status); + lvm->L = NULL; + return status; } - atomic_inc(&vm_nalloc); - atomic_inc(&vm_inuse); - return L; + lvm_stats_vmalloc(); + return 0; } -static void lua_state_free(lua_State *L) +static void lua_state_free(struct lvm_state *lvm) { - if (L) { - atomic_inc(&vm_nfree); - atomic_dec(&vm_inuse); - lua_close(L); + if (lvm->L) { + lvm_stats_vmfree(); + lua_close(lvm->L); + lvm->L = NULL; } } @@ -511,6 +613,7 @@ static void lua_module_free(struct lua_lsm_module *module) int lua_module_register(const char *code, size_t len) { struct lua_lsm_module *module, *m; + struct lvm_state lvm; lua_State *L; luaL_Buffer B; const char *chunk; @@ -520,10 +623,12 @@ int lua_module_register(const char *code, size_t len) int status; int err; - L = lua_state_alloc(NULL); - if (IS_ERR(L)) - return PTR_ERR(L); + memset(&lvm, 0, sizeof(struct lvm_state)); + err = lua_state_alloc(&lvm); + if (err) + return err; + L = lvm.L; lua_pushcfunction(L, lua_traceback); err = luaL_loadbuffer_wrap(L, code, len, ""); if (err) @@ -689,13 +794,13 @@ int lua_module_register(const char *code, size_t len) pr_info("module <%s> registered with %d filters\n", module->name, module->nhooks); - lua_state_free(L); + lua_state_free(&lvm); return 0; err_free_module: lua_module_free(module); err_free_lua: - lua_state_free(L); + lua_state_free(&lvm); return err; } @@ -797,7 +902,7 @@ static void softirq_lvm_remove_module(struct work_struct *work) * changing the Lua VM environment. */ local_bh_disable(); - err = lvm_remove_module(per_cpu(irq_lvms, cpu), module); + err = lvm_remove_module(per_cpu(irq_lvms, cpu)->L, module); if (!err) { atomic_inc(&work_ctx_remove_count); __log_info("<%s>: err = [ OK ] \t, count = %d\n", @@ -927,8 +1032,8 @@ int lua_module_unregister(const char *name) } mutex_unlock(&modules_mutex); - pr_info("Unregistered module <%s> from %d/%d Lua VMs, vm_inuse = %d\n", - name, count, nloaded, atomic_read(&vm_inuse)); + pr_info("Unregistered module <%s> from %d/%d Lua VMs, vm_nusage = %d\n", + name, count, nloaded, atomic_read(&vm_nusage)); return err; } @@ -959,114 +1064,30 @@ int modules_show(struct seq_file *m, void *v) return 0; } - -#ifdef CONFIG_SECURITY_LUA_LSM_STATISTICS - -int lua_lsm_status_show(struct seq_file *m, void *v) -{ - struct task_struct *g, *p; - unsigned int cpu; - int nalloc, nfree; - int inuse, total, minimum, maximum, avg; - int nbytes; - lua_State *L; - - nalloc = atomic_read(&vm_nalloc); - nfree = atomic_read(&vm_nfree); - seq_printf(m, "Lua VM:\n"); - seq_printf(m, " Alloc: alloc %d, free %d, inuse %d\n", - nalloc, nfree, nalloc - nfree); - seq_printf(m, " Mem alloc: alloc %d, realloc %d, free %d\n", - atomic_read(&mem_nalloc), atomic_read(&mem_nrealloc), - atomic_read(&mem_nfree)); - - inuse = 0; - total = 0; - minimum = INT_MAX; - maximum = 0; - mutex_lock(&modules_mutex); - read_lock(&tasklist_lock); - for_each_process_thread(g, p) { - L = lua_lsm_task(p)->L; - nbytes = lua_gc(L, LUA_GCCOUNT, 0) * 1024 + lua_gc(L, LUA_GCCOUNTB, 0); - inuse++; - total += nbytes; - minimum = min(minimum, nbytes); - maximum = max(maximum, nbytes); - } - read_unlock(&tasklist_lock); - - cpus_read_lock(); - for_each_possible_cpu(cpu) { - L = lua_lsm_task(idle_task(cpu))->L; - nbytes = lua_gc(L, LUA_GCCOUNT, 0) * 1024 + lua_gc(L, LUA_GCCOUNTB, 0); - inuse++; - total += nbytes; - minimum = min(minimum, nbytes); - maximum = max(maximum, nbytes); - } - cpus_read_unlock(); - mutex_unlock(&modules_mutex); - - avg = inuse ? total / inuse : 0; - seq_printf(m, " Mem usage: total %d, min %d, max %d, inuse %d, average %d\n", - total, minimum, maximum, inuse, avg); - - kvcache_status(&nalloc, &nfree); - seq_printf(m, "kvcache:\n"); - seq_printf(m, " Alloc: alloc %d, free %d, inuse %d\n", - nalloc, nfree, nalloc - nfree); - - return 0; -} - -int lsmhook_stat_show(struct seq_file *m, void *v) -{ - struct lua_lsm_hook_stat *stat; - int i = 1; - - seq_printf(m, "stats for lua-lsm (ns)\n"); - seq_printf(m, "%3s %-28s %4s %12s %15s %10s %12s\n", - "num", "name", "nlsm", "count", "total", "average", "maxtime"); - seq_printf(m, "%s\n", TABLINE); - - for (stat = lua_lsm_hook_stats; stat->name; stat++) { - int n = atomic_read(&stat->count); - s64 total = atomic64_read(&stat->time); - s64 maxtime = atomic64_read(&stat->maxtime); - seq_printf(m, "%3d %-28s %4d %12d %15llu %10llu %12llu\n", - i++, stat->name, atomic_read(&stat->nhooks), - n, total, n ? total / n : 0, maxtime); - } - return 0; -} - -#endif - /*********************************** main ***********************************/ int lua_task_blob_init(struct task_struct *task) { struct lua_lsm_task *llt = lua_lsm_task(task); - lua_State *L; + struct lvm_state *lvm = &llt->lvm; + int err; kvcache_dict_init(&llt->dict); - L = lua_state_alloc(NULL); - if (IS_ERR(L)) - return PTR_ERR(L); + refcount_init(&lvm->refcount, 0); + err = lua_state_alloc(lvm); + if (err) + return err; - llt->L = L; - refcount_init(&llt->refcount, 0); return 0; } void lua_task_blob_free(struct task_struct *task) { struct lua_lsm_task *llt = lua_lsm_task(task); - lua_modules_free(task, llt->L); - lua_state_free(llt->L); - llt->L = NULL; + struct lvm_state *lvm = &llt->lvm; + lua_modules_free(task, lvm->L); + lua_state_free(lvm); kvcache_dict_free(&llt->dict); } @@ -1106,7 +1127,7 @@ static const struct lsm_id lua_lsmid = { static int __init lua_lsm_init(void) { - lua_State *L; + struct lvm_state *lvm; int cpu; int err; @@ -1115,10 +1136,15 @@ static int __init lua_lsm_init(void) return err; for_each_possible_cpu(cpu) { - L = lua_state_alloc(&irq_lvms_ud); - if (IS_ERR(L)) - return PTR_ERR(L); - per_cpu(irq_lvms, cpu) = L; + lvm = kzalloc(sizeof(struct lvm_state), GFP_KERNEL); + if (lvm == NULL) + return -ENOMEM; + + err = lua_state_alloc(lvm); + if (err) + return err; + + per_cpu(irq_lvms, cpu) = lvm; } security_add_hooks(lua_lsm_hooks, ARRAY_SIZE(lua_lsm_hooks), &lua_lsmid); diff --git a/security/lua/lsm.h b/security/lua/lsm.h index d39670b59e3b..6b5830a25b9f 100644 --- a/security/lua/lsm.h +++ b/security/lua/lsm.h @@ -28,7 +28,7 @@ extern int lua_lsm_initialized __initdata; struct lua_lsm_hook_stat { const char *name; atomic_t nhooks; -#ifdef CONFIG_SECURITY_LUA_LSM_STATISTICS +#ifdef CONFIG_SECURITY_LUA_LSM_STATS atomic_t count; atomic64_t time; /* ns */ atomic64_t maxtime; @@ -103,16 +103,25 @@ int lua_module_unregister(const char *name); int modules_show(struct seq_file *m, void *v); -#ifdef CONFIG_SECURITY_LUA_LSM_STATISTICS -int lua_lsm_status_show(struct seq_file *m, void *v); -int lsmhook_stat_show(struct seq_file *m, void *v); +#ifdef CONFIG_SECURITY_LUA_LSM_STATS +void lvm_stats_show(struct seq_file *m); +int lsm_funcs_show(struct seq_file *m, void *v); #endif extern struct lsm_blob_sizes lua_lsm_blob_sizes; -struct lua_lsm_task { +struct lvm_state { lua_State *L; atomic_t refcount; +#ifdef CONFIG_SECURITY_LUA_LSM_STATS + atomic_t nalloc; + atomic_t nrealloc; + atomic_t nfree; +#endif +}; + +struct lua_lsm_task { + struct lvm_state lvm; struct kvcache_dict dict; }; diff --git a/security/lua/lsm_defs.h b/security/lua/lsm_defs.h index e1ea27d651fd..dd81bfa939ae 100644 --- a/security/lua/lsm_defs.h +++ b/security/lua/lsm_defs.h @@ -19,7 +19,7 @@ #undef LSM_HOOK -#ifdef CONFIG_SECURITY_LUA_LSM_STATISTICS +#ifdef CONFIG_SECURITY_LUA_LSM_STATS #define START_STATS(NAME) \ do { \ diff --git a/security/lua/securityfs.c b/security/lua/securityfs.c index cd21e7845220..045d617b3654 100644 --- a/security/lua/securityfs.c +++ b/security/lua/securityfs.c @@ -134,15 +134,22 @@ static const struct file_operations fops_module = { .release = single_release, }; -#ifdef CONFIG_SECURITY_LUA_LSM_STATISTICS +#ifdef CONFIG_SECURITY_LUA_LSM_STATS -static int open_status(struct inode *inode, struct file *filp) +static int stats_show(struct seq_file *m, void *v) { - return single_open(filp, lua_lsm_status_show, NULL); + lvm_stats_show(m); + kvcache_stats_show(m); + return 0; +} + +static int open_stats(struct inode *inode, struct file *filp) +{ + return single_open(filp, stats_show, NULL); } -static const struct file_operations fops_status = { - .open = open_status, +static const struct file_operations fops_stats = { + .open = open_stats, .read = seq_read, .llseek = seq_lseek, .release = single_release, @@ -150,7 +157,7 @@ static const struct file_operations fops_status = { static int open_lsm_funcs(struct inode *inode, struct file *filp) { - return single_open(filp, lsmhook_stat_show, NULL); + return single_open(filp, lsm_funcs_show, NULL); } static const struct file_operations fops_lsm_funcs = { @@ -172,8 +179,8 @@ static struct lua_file { { "register", 0222, &fops_register }, /* -w--w--w- */ { "unregister", 0222, &fops_unregister }, /* -w--w--w- */ { "module", 0444, &fops_module }, /* r--r--r-- */ -#ifdef CONFIG_SECURITY_LUA_LSM_STATISTICS - { "status", 0444, &fops_status }, /* r--r--r-- */ +#ifdef CONFIG_SECURITY_LUA_LSM_STATS + { "stats", 0444, &fops_stats }, /* r--r--r-- */ { "lsm_funcs", 0444, &fops_lsm_funcs }, /* r--r--r-- */ #endif { NULL, 0, NULL } From aaf2eb82cf92d68ae13d4312c91911a5a5bb1e87 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Tue, 16 Dec 2025 10:04:58 +0800 Subject: [PATCH 067/110] lua-lsm: refactor securityfs --- security/lua/lsm.c | 41 ++++++++++---------- security/lua/lsm.h | 8 ++-- security/lua/lsm_defs.c | 4 +- security/lua/securityfs.c | 79 ++++++++++++++++----------------------- 4 files changed, 58 insertions(+), 74 deletions(-) diff --git a/security/lua/lsm.c b/security/lua/lsm.c index a49c878a527b..2a917f0e8305 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -74,7 +74,7 @@ struct lua_lsm_hook_stat lua_lsm_hook_stats[] = { /********************************** stats **********************************/ -/* used by module unregister */ +/* Used by module unregister */ static atomic_t vm_nusage = ATOMIC_INIT(0); #ifdef CONFIG_SECURITY_LUA_LSM_STATS @@ -195,7 +195,7 @@ static inline void lvm_stats_memalloc(struct lvm_state *lvm, void *ptr, #endif -/********************************** Lua VM **********************************/ +/********************************** lvm **********************************/ static DEFINE_PER_CPU(struct lvm_state *, irq_lvms); @@ -243,6 +243,7 @@ void lvm_put(lua_State *L) put_cpu_var(irq_lvms); } +/********************************** sandbox **********************************/ static int lua_shared_index(lua_State *L) { @@ -326,7 +327,7 @@ static int lua_shared_newindex(lua_State *L) static int lua_module_fenv_newindex(lua_State *L) { /* args: t, k, v */ - __log_warn("warning: set global variable, [%s] %s - [%s] %s\n", + __log_warn("Warning: set global variable, [%s] %s - [%s] %s\n", luaL_typename(L, 2), lua_tostring(L, 2) ?: "(null)", luaL_typename(L, 3), lua_tostring(L, 3) ?: "(null)"); @@ -335,7 +336,7 @@ static int lua_module_fenv_newindex(lua_State *L) return 0; } -static int lua_module_load(lua_State *L, struct lua_lsm_module *module) +static int module_load(lua_State *L, struct lua_lsm_module *module) { int err; @@ -399,7 +400,7 @@ static int lua_module_load(lua_State *L, struct lua_lsm_module *module) return 0; } -static int lua_module_index(lua_State *L) +static int lua_modules_index(lua_State *L) { const char *key = luaL_checkstring(L, 2); struct lua_lsm_module *module; @@ -411,7 +412,7 @@ static int lua_module_index(lua_State *L) if (strcmp(module->name, key) != 0) continue; - err = lua_module_load(L, module); + err = module_load(L, module); if (err) { __log_err("load: %s, err = %d, top = %d\n", key, err, lua_gettop(L)); @@ -533,7 +534,7 @@ static int lvm_pmain(lua_State *L) /* build _MODULES table with metatable */ lua_newtable(L); /* _MODULES table */ lua_createtable(L, 0, 1); /* metatable */ - lua_pushcfunction(L, lua_module_index); /* TODO: pass it as args */ + lua_pushcfunction(L, lua_modules_index); /* TODO: pass it as args */ lua_setfield(L, -2, "__index"); /* metatable.__index = func */ /* setmetatable(_MODULES, metatable) */ lua_setmetatable(L, -2); @@ -600,7 +601,7 @@ static int lvm_writer(lua_State *L, const void *b, size_t size, void *B) return 0; } -static void lua_module_free(struct lua_lsm_module *module) +static void lua_lsm_module_free(struct lua_lsm_module *module) { kfree(module->chunk); kfree(module->name); @@ -610,7 +611,7 @@ static void lua_module_free(struct lua_lsm_module *module) kfree(module); } -int lua_module_register(const char *code, size_t len) +int lua_lsm_module_register(const char *code, size_t len) { struct lua_lsm_module *module, *m; struct lvm_state lvm; @@ -798,7 +799,7 @@ int lua_module_register(const char *code, size_t len) return 0; err_free_module: - lua_module_free(module); + lua_lsm_module_free(module); err_free_lua: lua_state_free(&lvm); @@ -911,7 +912,7 @@ static void softirq_lvm_remove_module(struct work_struct *work) local_bh_enable(); } -int lua_module_unregister(const char *name) +int lua_lsm_module_unregister(const char *name) { struct lua_lsm_module *module; struct lua_lsm_module_shdict *shdict, *tmp; @@ -1024,7 +1025,7 @@ int lua_module_unregister(const char *name) if (atomic_sub_return(count, &module->nloaded) == 0) { list_del_rcu(&module->list); - lua_module_free(module); + lua_lsm_module_free(module); err = 0; } else { module->state = LMS_STATE_ZOMBIE; @@ -1038,15 +1039,13 @@ int lua_module_unregister(const char *name) return err; } -/*****************************************************************************/ - int modules_show(struct seq_file *m, void *v) { struct lua_lsm_module *module; int idx; seq_printf(m, "modules for lua-lsm\n"); - seq_printf(m, "%-12s %-10s %6s %4s %5s %6s %6s %-34s\n", + seq_printf(m, "%-20s %-10s %6s %4s %5s %6s %6s %-34s\n", "name", "license", "size", "nlsm", "nload", "shdict", "kvnode", "author"); seq_printf(m, "%s\n", TABLINE); @@ -1054,7 +1053,7 @@ int modules_show(struct seq_file *m, void *v) idx = srcu_read_lock(&modules_ss); list_for_each_entry_srcu(module, &lsm_modules, list, srcu_read_lock_held(&modules_ss)) { - seq_printf(m, "%-12s %-10s %6zu %4d %5d %6d %6d %-34s\n", + seq_printf(m, "%-20s %-10s %6zu %4d %5d %6d %6d %-34s\n", module->name, module->license, module->chunk_len, module->nhooks, atomic_read(&module->nloaded), atomic_read(&module->shdict_count), @@ -1066,7 +1065,7 @@ int modules_show(struct seq_file *m, void *v) /*********************************** main ***********************************/ -int lua_task_blob_init(struct task_struct *task) +int task_blob_init(struct task_struct *task) { struct lua_lsm_task *llt = lua_lsm_task(task); struct lvm_state *lvm = &llt->lvm; @@ -1082,7 +1081,7 @@ int lua_task_blob_init(struct task_struct *task) return 0; } -void lua_task_blob_free(struct task_struct *task) +void task_blob_free(struct task_struct *task) { struct lua_lsm_task *llt = lua_lsm_task(task); struct lvm_state *lvm = &llt->lvm; @@ -1118,7 +1117,7 @@ static struct security_hook_list lua_lsm_hooks[] __ro_after_init = { #undef LSM_HOOK }; -int lua_enabled __ro_after_init = 1; +int lua_lsm_enabled __ro_after_init = 1; static const struct lsm_id lua_lsmid = { .name = "lua", @@ -1131,7 +1130,7 @@ static int __init lua_lsm_init(void) int cpu; int err; - err = lua_task_blob_init(current); + err = task_blob_init(current); if (err) return err; @@ -1158,7 +1157,7 @@ static int __init lua_lsm_init(void) DEFINE_LSM(lua) = { .name = "lua", - .enabled = &lua_enabled, + .enabled = &lua_lsm_enabled, .blobs = &lua_lsm_blob_sizes, .init = lua_lsm_init, }; diff --git a/security/lua/lsm.h b/security/lua/lsm.h index 6b5830a25b9f..6a86b4f42b21 100644 --- a/security/lua/lsm.h +++ b/security/lua/lsm.h @@ -98,8 +98,8 @@ extern struct srcu_struct modules_ss; lua_State *lvm_get(void); void lvm_put(lua_State *L); -int lua_module_register(const char *code, size_t len); -int lua_module_unregister(const char *name); +int lua_lsm_module_register(const char *code, size_t len); +int lua_lsm_module_unregister(const char *name); int modules_show(struct seq_file *m, void *v); @@ -203,8 +203,8 @@ static inline struct lua_lsm_object *lua_lsm_bdev(const struct block_device *bde return bdev->bd_security + lua_lsm_blob_sizes.lbs_bdev; } -int lua_task_blob_init(struct task_struct *task); -void lua_task_blob_free(struct task_struct *task); +int task_blob_init(struct task_struct *task); +void task_blob_free(struct task_struct *task); /* lua C module */ diff --git a/security/lua/lsm_defs.c b/security/lua/lsm_defs.c index d050a66ebe48..4bb5614b7b0f 100644 --- a/security/lua/lsm_defs.c +++ b/security/lua/lsm_defs.c @@ -1591,7 +1591,7 @@ LUA_LSM_INT_DEFINE1(file_truncate, struct file *, file) LUA_LSM_PREPARE_DEFINE2(task_alloc, struct task_struct *, task, unsigned long, clone_flags) { - return lua_task_blob_init(task); + return task_blob_init(task); } /** @@ -1610,7 +1610,7 @@ LUA_LSM_INT_DEFINE2(task_alloc, struct task_struct *, task, */ LUA_LSM_POSTPONE_DEFINE1(task_free, struct task_struct *, task) { - lua_task_blob_free(task); + task_blob_free(task); } /** diff --git a/security/lua/securityfs.c b/security/lua/securityfs.c index 045d617b3654..21947c8b5c12 100644 --- a/security/lua/securityfs.c +++ b/security/lua/securityfs.c @@ -11,7 +11,7 @@ #include "lsm.h" -static bool lua_capable(int cap) +static bool lua_lsm_capable(int cap) { bool allow = true; int rc; @@ -49,16 +49,14 @@ static const struct file_operations fops_version = { .release = single_release, }; - -static ssize_t register_write(struct file *file, const char __user *buf, - size_t len, loff_t *ppos) +static ssize_t module_write(const char __user *buf, size_t len, + loff_t *ppos, int load) { - char *buffer; + char *buffer, *p; int err; - __log_info("len = %d, %pD\n", (int)len, file); - - if (!lua_capable(CAP_MAC_ADMIN)) + __log_info("len = %d\n", (int)len); + if (!lua_lsm_capable(CAP_MAC_ADMIN)) return -EPERM; /* No partial writes. */ @@ -71,8 +69,17 @@ static ssize_t register_write(struct file *file, const char __user *buf, if (IS_ERR(buffer)) return PTR_ERR(buffer); - __log_info("buffer = %s [%d]\n", buffer, (int)len); - err = lua_module_register(buffer, len); + __log_info("buffer = [%d] %s\n", (int)len, buffer); + if (load) { + err = lua_lsm_module_register(buffer, len); + } else { + /* remove the tailing '\n' */ + p = &buffer[len - 1]; + while (p >= buffer && *p == '\n') + *p-- = '\0'; + + err = lua_lsm_module_unregister(buffer); + } kfree(buffer); if (err < 0) return err; @@ -80,55 +87,33 @@ static ssize_t register_write(struct file *file, const char __user *buf, return len; } +static ssize_t register_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + return module_write(buf, len, ppos, 1); +} + static const struct file_operations fops_register = { .write = register_write, }; - static ssize_t unregister_write(struct file *file, const char __user *buf, size_t len, loff_t *ppos) { - char *buffer, *p; - int err; - - if (!lua_capable(CAP_MAC_ADMIN)) - return -EPERM; - - /* No partial writes. */ - if (*ppos != 0) - return -EINVAL; - if (len == 0) - return -EINVAL; - - buffer = memdup_user_nul(buf, len); - if (IS_ERR(buffer)) - return PTR_ERR(buffer); - - /* remove the tailing '\n' */ - p = &buffer[len - 1]; - while (p >= buffer && *p == '\n') - *p-- = '\0'; - __log_info("buffer = %s [%d]\n", buffer, (int)len); - err = lua_module_unregister(buffer); - kfree(buffer); - if (err < 0) - return err; - - return len; + return module_write(buf, len, ppos, 0); } static const struct file_operations fops_unregister = { .write = unregister_write, }; - -static int open_module(struct inode *inode, struct file *filp) +static int open_modules(struct inode *inode, struct file *filp) { return single_open(filp, modules_show, NULL); } -static const struct file_operations fops_module = { - .open = open_module, +static const struct file_operations fops_modules = { + .open = open_modules, .read = seq_read, .llseek = seq_lseek, .release = single_release, @@ -169,7 +154,7 @@ static const struct file_operations fops_lsm_funcs = { #endif -static struct lua_file { +static struct lua_lsm_file { const char *name; umode_t mode; const struct file_operations *fops; @@ -178,7 +163,7 @@ static struct lua_file { { "version", 0444, &fops_version }, /* r--r--r-- */ { "register", 0222, &fops_register }, /* -w--w--w- */ { "unregister", 0222, &fops_unregister }, /* -w--w--w- */ - { "module", 0444, &fops_module }, /* r--r--r-- */ + { "modules", 0444, &fops_modules }, /* r--r--r-- */ #ifdef CONFIG_SECURITY_LUA_LSM_STATS { "stats", 0444, &fops_stats }, /* r--r--r-- */ { "lsm_funcs", 0444, &fops_lsm_funcs }, /* r--r--r-- */ @@ -186,11 +171,11 @@ static struct lua_file { { NULL, 0, NULL } }; -static int __init lua_securityfs_init(void) +static int __init lua_lsm_securityfs_init(void) { struct dentry *dir; struct dentry *dentry; - struct lua_file *file; + struct lua_lsm_file *file; if (!lua_lsm_initialized) return 0; @@ -217,4 +202,4 @@ static int __init lua_securityfs_init(void) return 0; } -fs_initcall(lua_securityfs_init); +fs_initcall(lua_lsm_securityfs_init); From 16a5eaae154cb600d131e6b8f4b33fd544e8ab89 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Wed, 17 Dec 2025 14:59:22 +0800 Subject: [PATCH 068/110] lua-lsm: api - Keep semantic consistency with math.random --- security/lua/lua_kernel.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/security/lua/lua_kernel.c b/security/lua/lua_kernel.c index 1dd8da69655f..8133bb6b24ef 100644 --- a/security/lua/lua_kernel.c +++ b/security/lua/lua_kernel.c @@ -331,12 +331,14 @@ static int kernel_random(lua_State *L) case 1: u = luaL_checkint(L, 1); luaL_argcheck(L, 0 <= u, 1, "interval is empty"); - r = get_random_u32_below((u32)u); + /* r: [1, u] */ + r = get_random_u32_below((u32)u) + 1; break; case 2: l = luaL_checkint(L, 1); u = luaL_checkint(L, 2); luaL_argcheck(L, l <= u, 2, "interval is empty"); + /* r: [l, u] */ r = get_random_u32_inclusive((u32)l, (u32)u); break; default: From 6a4e278a35bad8d41e825024840025b61f11433f Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Thu, 18 Dec 2025 17:41:27 +0800 Subject: [PATCH 069/110] lua-lsm: add has_kvcache result for type method --- security/lua/lua_object.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/security/lua/lua_object.h b/security/lua/lua_object.h index 4bcff36af61a..599899e07729 100644 --- a/security/lua/lua_object.h +++ b/security/lua/lua_object.h @@ -44,11 +44,12 @@ return lua_object_ ## fname(L, &ll->dict); \ } -#define LUA_OBJECT_FUNCS_DEFINE(name, ctype) \ +#define LUA_OBJECT_FUNCS_DEFINE(name, ctype, has_kvcache) \ static int rawmeth_ ## name ## _type(lua_State *L) \ { \ lua_pushstring(L, #name); \ - return 1; \ + lua_pushboolean(L, has_kvcache); \ + return 2; \ } \ static int rawmeth_ ## name ## _tostring(lua_State *L) \ { \ @@ -65,7 +66,7 @@ LUA_OBJECT_KVCACHE_FUNC(name, ctype, blob, kvcache_incr, incr) \ LUA_OBJECT_KVCACHE_FUNC(name, ctype, blob, index, index) \ LUA_OBJECT_KVCACHE_FUNC(name, ctype, blob, newindex, newindex) \ - LUA_OBJECT_FUNCS_DEFINE(name, ctype) \ + LUA_OBJECT_FUNCS_DEFINE(name, ctype, 1) \ static inline void create_ ## name ## _meta(lua_State *L, \ const luaL_Reg *funcs, const luaL_Reg *gc) \ { \ @@ -90,7 +91,7 @@ LUA_OBJECT_META_DEFINE(name, ctype, d, METHOD_NAME(name)) \ LUA_OBJECT_META_DEFINE(raw ## name, ctype, d, METHOD_NAME_RAW(name)) \ LUA_OBJECT_META_DEFINE(gc ## name, ctype, d, METHOD_NAME_GC(name)) \ - LUA_OBJECT_FUNCS_DEFINE(name, ctype) \ + LUA_OBJECT_FUNCS_DEFINE(name, ctype, 0) \ static inline void create_ ## name ## _meta(lua_State *L, \ const luaL_Reg *funcs, const luaL_Reg *gc) \ { \ From faef6602da20555859be610bce48170891ef05d4 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Tue, 23 Dec 2025 11:00:59 +0800 Subject: [PATCH 070/110] lua-lsm: extend api --- security/lua/auxlib.c | 105 ++++++++++++++++++++++++++++++ security/lua/auxlib.h | 3 + security/lua/kvcache.c | 13 +--- security/lua/lsm_defs.c | 21 ++++-- security/lua/lua_capability.c | 21 ++---- security/lua/lua_kernel.c | 119 +++++++++++++++++++++++++++++++--- security/lua/lua_object.h | 2 + 7 files changed, 244 insertions(+), 40 deletions(-) diff --git a/security/lua/auxlib.c b/security/lua/auxlib.c index b1947ba633d1..23e414a3834b 100644 --- a/security/lua/auxlib.c +++ b/security/lua/auxlib.c @@ -13,10 +13,13 @@ #include #include #include +#include +#include #include #include #include #include "auxlib.h" +#include "lua_object.h" void lua_table_dump(lua_State *L, int idx, int level, int max) @@ -462,3 +465,105 @@ int aux_dentry_path(lua_State *L, struct dentry *dentry, int rawpath) kfree(buf); return nres; } + +int arg2cap(lua_State *L, int idx) +{ + static const struct cflag_opt caps[] = { + { "chown", CAP_CHOWN }, + { "dac_override", CAP_DAC_OVERRIDE }, + { "dac_read_search", CAP_DAC_READ_SEARCH }, + { "fowner", CAP_FOWNER }, + { "fsetid", CAP_FSETID }, + { "kill", CAP_KILL }, + { "setgid", CAP_SETGID }, + { "setuid", CAP_SETUID }, + { "setpcap", CAP_SETPCAP }, + { "linux_immutable", CAP_LINUX_IMMUTABLE }, + { "net_bind_service", CAP_NET_BIND_SERVICE }, + { "net_broadcast", CAP_NET_BROADCAST }, + { "net_admin", CAP_NET_ADMIN }, + { "net_raw", CAP_NET_RAW }, + { "ipc_lock", CAP_IPC_LOCK }, + { "ipc_owner", CAP_IPC_OWNER }, + { "sys_module", CAP_SYS_MODULE }, + { "sys_rawio", CAP_SYS_RAWIO }, + { "sys_chroot", CAP_SYS_CHROOT }, + { "sys_ptrace", CAP_SYS_PTRACE }, + { "sys_pacct", CAP_SYS_PACCT }, + { "sys_admin", CAP_SYS_ADMIN }, + { "sys_boot", CAP_SYS_BOOT }, + { "sys_nice", CAP_SYS_NICE }, + { "sys_resource", CAP_SYS_RESOURCE }, + { "sys_time", CAP_SYS_TIME }, + { "sys_tty_config", CAP_SYS_TTY_CONFIG }, + { "mknod", CAP_MKNOD }, + { "lease", CAP_LEASE }, + { "audit_write", CAP_AUDIT_WRITE }, + { "audit_control", CAP_AUDIT_CONTROL }, + { "setfcap", CAP_SETFCAP }, + { "mac_override", CAP_MAC_OVERRIDE }, + { "mac_admin", CAP_MAC_ADMIN }, + { "syslog", CAP_SYSLOG }, + { "wake_alarm", CAP_WAKE_ALARM }, + { "block_suspend", CAP_BLOCK_SUSPEND }, + { "audit_read", CAP_AUDIT_READ }, + { "perfmon", CAP_PERFMON }, + { "bpf", CAP_BPF }, + { "checkpoint_restore", CAP_CHECKPOINT_RESTORE }, + { NULL, 0 } + }; + int tt = lua_type(L, idx); + int cap; + switch (tt) { + case LUA_TNUMBER: + cap = luaL_checkinteger(L, idx); + break; + case LUA_TSTRING: + cap = (int)tocflags(L, idx, idx, caps, -1); + if (cap == -1) + return luaL_argerror(L, idx, "invalid capability"); + break; + default: + return luaL_argerror(L, idx, "integer or string expected"); + } + return cap; +} + +/* + * [task:]capable(CAP_MAC_ADMIN) + * [task:]capable('mac_admin') + * [task:]capable(task, 'mac_admin') + * [task:]capable(task, 'mac_admin', 'noaudit', 'insetid') + */ +int aux_capable(lua_State *L, const struct cred *cred, int idx) +{ + static const struct cflag_opt opts[] = { + { "noaudit", CAP_OPT_NOAUDIT }, + { "insetid", CAP_OPT_INSETID }, + { NULL, 0 } + }; + unsigned int opt = CAP_OPT_NONE; + int top = lua_gettop(L); + int cap; + int err; + + if (top < idx) + return luaL_error(L, "At least %d argument is required.", idx); + + if (top == idx) { + cap = arg2cap(L, idx); + err = cap_capable(cred, current_user_ns(), cap, opt); + } else { + struct task_struct *task = totask(L, idx); + cap = arg2cap(L, idx + 1); + if (top >= idx + 2) + opt = tocflags(L, idx + 2, top, opts, CAP_OPT_NONE); + + rcu_read_lock(); + err = cap_capable(cred, __task_cred(task)->user_ns, cap, opt); + rcu_read_unlock(); + } + + lua_pushboolean(L, err == 0); + return 1; +} diff --git a/security/lua/auxlib.h b/security/lua/auxlib.h index 39b83eecb7ae..429df31d1f8e 100644 --- a/security/lua/auxlib.h +++ b/security/lua/auxlib.h @@ -84,4 +84,7 @@ void setconst(lua_State *L, const struct const_value *cv); int aux_file_path(lua_State *L, struct file *filp); int aux_dentry_path(lua_State *L, struct dentry *dentry, int rawpath); +int arg2cap(lua_State *L, int idx); +int aux_capable(lua_State *L, const struct cred *cred, int idx); + #endif /* ! _SECURITY_LUA_LSM_AUXLIB_H */ diff --git a/security/lua/kvcache.c b/security/lua/kvcache.c index a621446c1f5f..a31bb6cfaf33 100644 --- a/security/lua/kvcache.c +++ b/security/lua/kvcache.c @@ -8,6 +8,7 @@ #include "debug.h" #include #include +#include #include #include #include @@ -61,18 +62,8 @@ RB_GENERATE_STATIC(kvcache, kvcache_node, node, kvcache_node_cmp); static int kvcache_result(lua_State *L, int err) { - const char *error; - - switch (err) { - case -ENOMEM: error = "no memory"; break; - case -EINVAL: error = "invalid"; break; - case -ERANGE: error = "no space"; break; - case -ESRCH: error = "no module"; break; - case -EEXIST: error = "exists"; break; - default: error = "unknown"; break; - } lua_pushnil(L); - lua_pushstring(L, error); + lua_pushstring(L, errname(err) ?: "unknown"); return 2; } diff --git a/security/lua/lsm_defs.c b/security/lua/lsm_defs.c index 4bb5614b7b0f..0747747b4e09 100644 --- a/security/lua/lsm_defs.c +++ b/security/lua/lsm_defs.c @@ -1990,11 +1990,20 @@ LUA_LSM_INT_DEFINE4(task_kill, struct task_struct *, p, LUA_LSM_INT_DEFINE5(task_prctl, int, option, unsigned long, arg2, unsigned long, arg3, unsigned long, arg4, unsigned long, arg5) { - lua_pushinteger(L, (lua_Integer)option); - lua_pushnumber(L, (lua_Number)arg2); - lua_pushnumber(L, (lua_Number)arg3); - lua_pushnumber(L, (lua_Number)arg4); - lua_pushnumber(L, (lua_Number)arg5); + switch (option) { + case PR_SET_PTRACER: + lua_pushstring(L, "set_ptracer"); + lua_pushinteger(L, (lua_Integer)arg2); + break; + + default: + lua_pushinteger(L, (lua_Integer)option); + lua_pushnumber(L, (lua_Number)arg2); + lua_pushnumber(L, (lua_Number)arg3); + lua_pushnumber(L, (lua_Number)arg4); + lua_pushnumber(L, (lua_Number)arg5); + break; + } } /** @@ -2727,7 +2736,7 @@ LUA_LSM_INT_DEFINE3(socket_getpeersec_dgram, struct socket *, sock, struct sk_buff *, skb, u32 *, secid) { *newsocket(L) = sock; - *newskb(L) = skb; + skb ? *newskb(L) = skb : lua_pushnil(L); lua_pushnil(L); /* TODO: secid */ } diff --git a/security/lua/lua_capability.c b/security/lua/lua_capability.c index 03a2e87346f1..d2c547bb4483 100644 --- a/security/lua/lua_capability.c +++ b/security/lua/lua_capability.c @@ -67,14 +67,12 @@ static int capability_add(lua_State *L) *newcap(L) = cap_combine(cap1, cap2); settopfenvfrom(L, 1); return 1; - } else if (tt == LUA_TNUMBER) { - int flag = luaL_checkint(L, 2); - cap_raise(cap1, flag); + } else { + int cap = arg2cap(L, 2); + cap_raise(cap1, cap); *newcap(L) = cap1; settopfenvfrom(L, 1); return 1; - } else { - return 0; } } @@ -87,14 +85,12 @@ static int capability_sub(lua_State *L) *newcap(L) = cap_drop(cap1, cap2); settopfenvfrom(L, 1); return 1; - } else if (tt == LUA_TNUMBER) { - int flag = luaL_checkint(L, 2); - cap_lower(cap1, flag); + } else { + int cap = arg2cap(L, 2); + cap_lower(cap1, cap); *newcap(L) = cap1; settopfenvfrom(L, 1); return 1; - } else { - return 0; } } @@ -160,10 +156,7 @@ static int capability_cap_full(lua_State *L) static int capability_capable(lua_State *L) { - int cap = luaL_checkinteger(L, 1); - int err = cap_capable(current_cred(), &init_user_ns, cap, CAP_OPT_NONE); - lua_pushboolean(L, err == 0); - return 1; + return aux_capable(L, current_cred(), 1); } static const luaL_Reg capabilitylib[] = { diff --git a/security/lua/lua_kernel.c b/security/lua/lua_kernel.c index 8133bb6b24ef..dbdf83cba49d 100644 --- a/security/lua/lua_kernel.c +++ b/security/lua/lua_kernel.c @@ -200,14 +200,18 @@ static int kernel_task_same_thread_group(lua_State *L) return 1; } -static int kernel_task_ptrace_parent(lua_State *L) +static int kernel_task_same_group_ptracer(lua_State *L) { - struct task_struct *task = totask(L, 1); - struct task_struct *parent = ptrace_parent(task); - if (parent == NULL) - return 0; - *newtask(L) = parent; - settopfenvfrom(L, 1); + struct task_struct *tracee = totask(L, 1); + struct task_struct *tracer = totask(L, 2); + struct task_struct *parent; + int res = 0; + rcu_read_lock(); + parent = ptrace_parent(tracee); + if (parent && same_thread_group(parent, tracer)) + res = 1; + rcu_read_unlock(); + lua_pushboolean(L, res); return 1; } @@ -258,6 +262,57 @@ static int kernel_task_cmdline(lua_State *L) return 1; } +static int kernel_task_capable(lua_State *L) +{ + struct task_struct *task = totask(L, 1); + int nres; + rcu_read_lock(); + nres = aux_capable(L, __task_cred(task), 2); + rcu_read_unlock(); + return nres; +} + +static int kernel_task_is_descendant(lua_State *L) +{ + struct task_struct *child = totask(L, 1); + struct task_struct *parent; + int tt = lua_type(L, 2); + int res = 0; + + switch (tt) { + case LUA_TNUMBER: + parent = find_get_task_by_vpid((pid_t)lua_tointeger(L, 2)); + if (parent == NULL) + return luaL_argerror(L, 2, "invalid pid"); + break; + case LUA_TUSERDATA: + parent = totask(L, 2); + break; + default: + return luaL_argerror(L, 2, "task or pid expected"); + } + + rcu_read_lock(); + if (!thread_group_leader(parent)) + parent = rcu_dereference(parent->group_leader); + while (child->pid > 0) { + if (!thread_group_leader(child)) + child = rcu_dereference(child->group_leader); + if (child == parent) { + res = 1; + break; + } + child = rcu_dereference(child->real_parent); + } + rcu_read_unlock(); + + if (tt == LUA_TNUMBER) + put_task_struct(parent); + + lua_pushboolean(L, res); + return 1; +} + static int meth_task_tostring(lua_State *L) { struct task_struct *task = totask(L, 1); @@ -273,15 +328,33 @@ static const luaL_Reg task_meth[] = { { "group_leader", kernel_task_group_leader }, { "thread_group_leader", kernel_task_thread_group_leader }, { "same_thread_group", kernel_task_same_thread_group }, - { "ptrace_parent", kernel_task_ptrace_parent }, + { "same_group_ptracer", kernel_task_same_group_ptracer }, { "is_idle", kernel_task_is_idle }, { "exe_file", kernel_task_exe_file }, { "exepath", kernel_task_exepath }, { "cmdline", kernel_task_cmdline }, + { "capable", kernel_task_capable }, + { "is_descendant", kernel_task_is_descendant }, { "__tostring", meth_task_tostring }, { NULL, NULL } }; +static int meth_task_gc(lua_State *L) +{ + struct task_struct **taskp = togctaskp(L, 1); + if (*taskp) { + put_task_struct(*taskp); + *taskp = NULL; + } + return 0; +} + +static const luaL_Reg task_gc_meth[] = { + { "__tostring", meth_task_tostring }, + { "__gc", meth_task_gc }, + { NULL, NULL } +}; + /********************************** lib **********************************/ static int kernel_version(lua_State *L) @@ -360,6 +433,31 @@ static int kernel_ktime_seconds(lua_State *L) return 1; } +static int kernel_rcu_read_lock(lua_State *L) +{ + rcu_read_lock(); + return 0; +} + +static int kernel_rcu_read_unlock(lua_State *L) +{ + rcu_read_unlock(); + return 0; +} + +static int kernel_task_from_pid(lua_State *L) +{ + pid_t nr = (pid_t)luaL_checkinteger(L, 1); + struct task_struct *task = find_get_task_by_vpid(nr); + if (task == NULL) + return 0; + *newgctask(L) = task; + /* FIXME + settopfenvfrom(L, 1); + */ + return 1; +} + static int kernel_printk(lua_State *L) { const char *s = luaL_checkstring(L, 1); @@ -396,6 +494,9 @@ static const luaL_Reg kernellib[] = { { "lsm_funcs", kernel_lsm_funcs }, { "random", kernel_random }, { "ktime_seconds", kernel_ktime_seconds }, + { "rcu_read_lock", kernel_rcu_read_lock }, + { "rcu_read_unlock", kernel_rcu_read_unlock }, + { "task_from_pid", kernel_task_from_pid }, { "printk", kernel_printk }, #define XX(name) { "pr_" #name, kernel_pr_ ## name }, @@ -408,7 +509,7 @@ static const luaL_Reg kernellib[] = { LUALIB_API int luaopen_kernel(lua_State *L) { luaL_newlib(L, kernellib); - create_task_meta(L, task_meth, NULL); + create_task_meta(L, task_meth, task_gc_meth); create_cred_meta(L, cred_meth, NULL); create_perfevent_meta(L, NULL, NULL); create_ipc_meta(L, NULL, NULL); diff --git a/security/lua/lua_object.h b/security/lua/lua_object.h index 599899e07729..6c29b003753b 100644 --- a/security/lua/lua_object.h +++ b/security/lua/lua_object.h @@ -9,6 +9,8 @@ #define _SECURITY_LUA_LSM_LUA_OBJECT_H #include "auxlib.h" +#include "lsm.h" +#include "kvcache.h" #define METHOD_NAME(name) ("method." #name) #define METHOD_NAME_RAW(name) ("method." #name ".raw") From c430d42a324b40072bd245212257f9eecb552268 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Tue, 23 Dec 2025 20:07:05 +0800 Subject: [PATCH 071/110] lua-lsm: silent key/ib/tundev/perfevent kvcache --- security/lua/lsm.c | 15 +++++++++++---- security/lua/lsm.h | 5 +++-- security/lua/lsm_defs.c | 28 ++++++++++++++-------------- security/lua/lua_object.h | 6 ++++-- 4 files changed, 32 insertions(+), 22 deletions(-) diff --git a/security/lua/lsm.c b/security/lua/lsm.c index 2a917f0e8305..ba178b15a856 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -1090,21 +1090,28 @@ void task_blob_free(struct task_struct *task) kvcache_dict_free(&llt->dict); } +/* + * TODO: Currently, the key, perf_event, tun_dev, and ib objects do not + * have corresponding free LSM callback functions, which prevents object + * properties from being released. Therefore, setting properties for these + * four objects is not supported at this time, as this requires support at + * the security subsystem architecture. + */ struct lsm_blob_sizes lua_lsm_blob_sizes __ro_after_init = { .lbs_task = sizeof(struct lua_lsm_task), .lbs_cred = sizeof(struct lua_lsm_object), .lbs_file = sizeof(struct lua_lsm_object), - .lbs_ib = sizeof(struct lua_lsm_object), + .lbs_ib = 0, .lbs_inode = sizeof(struct lua_lsm_object), .lbs_sock = sizeof(struct lua_lsm_object), .lbs_superblock = sizeof(struct lua_lsm_object), .lbs_ipc = sizeof(struct lua_lsm_object), - .lbs_key = sizeof(struct lua_lsm_object), + .lbs_key = 0, .lbs_msg_msg = sizeof(struct lua_lsm_object), - .lbs_perf_event = sizeof(struct lua_lsm_object), + .lbs_perf_event = 0, /* TODO: number of xattr slots in new_xattrs array */ .lbs_xattr_count = 10, - .lbs_tun_dev = sizeof(struct lua_lsm_object), + .lbs_tun_dev = 0, .lbs_bdev = sizeof(struct lua_lsm_object), }; diff --git a/security/lua/lsm.h b/security/lua/lsm.h index 6a86b4f42b21..0ca761a86db7 100644 --- a/security/lua/lsm.h +++ b/security/lua/lsm.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "bitmap.h" #include "kvcache.h" @@ -188,9 +189,9 @@ static inline struct lua_lsm_object *lua_lsm_msgmsg(const struct msg_msg *msg) return msg->security + lua_lsm_blob_sizes.lbs_msg_msg; } -static inline struct lua_lsm_object *lua_lsm_perfevent(void *perf_event) +static inline struct lua_lsm_object *lua_lsm_perfevent(const struct perf_event *event) { - return perf_event + lua_lsm_blob_sizes.lbs_perf_event; + return event->security + lua_lsm_blob_sizes.lbs_perf_event; } static inline struct lua_lsm_object *lua_lsm_tun_dev(void *security) diff --git a/security/lua/lsm_defs.c b/security/lua/lsm_defs.c index 0747747b4e09..491e94f5cd44 100644 --- a/security/lua/lsm_defs.c +++ b/security/lua/lsm_defs.c @@ -2885,12 +2885,12 @@ LUA_LSM_VOID_DEFINE2(req_classify_flow, const struct request_sock *, req, } /** - * TODO: tun_dev_alloc_security + * tun_dev_alloc_security * Default: 0 */ LUA_LSM_INT_DEFINE1(tun_dev_alloc_security, void *, security) { - lua_pushnil(L); /* TODO: security */ + *newtundev(L) = security; } /** @@ -2903,31 +2903,31 @@ LUA_LSM_INT_DEFINE0(tun_dev_create) } /** - * TODO: tun_dev_attach_queue + * tun_dev_attach_queue * Default: 0 */ LUA_LSM_INT_DEFINE1(tun_dev_attach_queue, void *, security) { - lua_pushnil(L); /* TODO: security */ + *newtundev(L) = security; } /** - * TODO: tun_dev_attach + * tun_dev_attach * Default: 0 */ LUA_LSM_INT_DEFINE2(tun_dev_attach, struct sock *, sk, void *, security) { *newsock(L) = sk; - lua_pushnil(L); /* TODO: security */ + *newtundev(L) = security; } /** - * TODO: tun_dev_open + * tun_dev_open * Default: 0 */ LUA_LSM_INT_DEFINE1(tun_dev_open, void *, security) { - lua_pushnil(L); /* TODO: security */ + *newtundev(L) = security; } /** @@ -2991,35 +2991,35 @@ LUA_LSM_INT_DEFINE2(mptcp_add_subflow, struct sock *, sk, struct sock *, ssk) #ifdef CONFIG_SECURITY_INFINIBAND /** - * TODO: ib_pkey_access + * ib_pkey_access * Default: 0 */ LUA_LSM_INT_DEFINE3(ib_pkey_access, void *, sec, u64, subnet_prefix, u16, pkey) { - lua_pushnil(L); /* TODO: sec */ + *newib(L) = sec; lua_pushnumber(L, (lua_Number)subnet_prefix); lua_pushinteger(L, (lua_Integer)pkey); } /** - * TODO: ib_endport_manage_subnet + * ib_endport_manage_subnet * Default: 0 */ LUA_LSM_INT_DEFINE3(ib_endport_manage_subnet, void *, sec, const char *, dev_name, u8, port_num) { - lua_pushnil(L); /* TODO: sec */ + *newib(L) = sec; lua_pushstring(L, dev_name); lua_pushinteger(L, (lua_Integer)port_num); } /** - * TODO: ib_alloc_security + * ib_alloc_security * Default: 0 */ LUA_LSM_INT_DEFINE1(ib_alloc_security, void *, sec) { - lua_pushnil(L); /* TODO: sec */ + *newib(L) = sec; } #endif /* CONFIG_SECURITY_INFINIBAND */ diff --git a/security/lua/lua_object.h b/security/lua/lua_object.h index 6c29b003753b..90c164dcb28b 100644 --- a/security/lua/lua_object.h +++ b/security/lua/lua_object.h @@ -118,14 +118,16 @@ #define LUA_OBJECTS_LIST \ LUA_OBJECT(task, kernel, task, struct task_struct *, NULL) \ LUA_OBJECT(object, kernel, cred, struct cred *, NULL) \ - LUA_OBJECT(object, kernel, perfevent, struct perf_event *, NULL) \ + LUA_OBJECT(func, kernel, perfevent, struct perf_event *, NULL) \ LUA_OBJECT(object, ipc, ipc, struct kern_ipc_perm *, NULL) \ LUA_OBJECT(object, ipc, msgmsg, struct msg_msg *, NULL) \ LUA_OBJECT(object, net, sock, struct sock *, NULL) \ + LUA_OBJECT(func, net, ib, void *, NULL) \ + LUA_OBJECT(func, net, tundev, void *, NULL) \ LUA_OBJECT(func, net, socket, struct socket *, NULL) \ LUA_OBJECT(func, net, skb, struct sk_buff *, NULL) \ LUA_OBJECT(func, net, sockaddr, struct sockaddr *, NULL) \ - LUA_OBJECT(object, security, key, struct key *, NULL) \ + LUA_OBJECT(func, security, key, struct key *, NULL) \ LUA_OBJECT(object, block, bdev, struct block_device *, NULL) \ LUA_OBJECT(object, fs, inode, struct inode *, NULL) \ LUA_OBJECT(object, fs, file, struct file *, NULL) \ From 656f37b892e605bebd7eb36a6cc7ec91fa079eee Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Wed, 24 Dec 2025 17:23:55 +0800 Subject: [PATCH 072/110] fix(lua-lsm): Resolve missing header dependencies for x86_64 build Explicitly include in lsm_defs.c to fix an "undeclared identifier" error for PR_SET_PTRACER. Signed-off-by: Zongyao Chen --- security/lua/lsm_defs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/security/lua/lsm_defs.c b/security/lua/lsm_defs.c index 491e94f5cd44..2de3559fa2a6 100644 --- a/security/lua/lsm_defs.c +++ b/security/lua/lsm_defs.c @@ -16,6 +16,7 @@ #include #include #include +#include #include /* for __MAP */ #include /* for ktime_get */ #include From 0d72757c3d30899f08606491f824744e0a0689d1 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Fri, 16 Jan 2026 16:55:19 +0800 Subject: [PATCH 073/110] lua-lsm: add pid_alive method & inode_init_security_anon hook --- security/lua/lsm_defs.c | 6 +++--- security/lua/lua_kernel.c | 8 ++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/security/lua/lsm_defs.c b/security/lua/lsm_defs.c index 2de3559fa2a6..539ff06e35c0 100644 --- a/security/lua/lsm_defs.c +++ b/security/lua/lsm_defs.c @@ -833,15 +833,15 @@ LUA_LSM_INT_NAKED_DEFINE5(inode_init_security, struct inode *, inode, } /** - * TODO: inode_init_security_anon + * inode_init_security_anon * Default: 0 */ LUA_LSM_INT_DEFINE3(inode_init_security_anon, struct inode *, inode, const struct qstr *, name, const struct inode *, context_inode) { *newinode(L) = inode; - lua_pushnil(L); /* TODO: name */ - *(const struct inode **)newinode(L) = context_inode; + lua_pushlstring(L, name->name, name->len); + context_inode ? *(const struct inode **)newinode(L) = context_inode : lua_pushnil(L); } /** diff --git a/security/lua/lua_kernel.c b/security/lua/lua_kernel.c index dbdf83cba49d..075cdf464514 100644 --- a/security/lua/lua_kernel.c +++ b/security/lua/lua_kernel.c @@ -313,6 +313,13 @@ static int kernel_task_is_descendant(lua_State *L) return 1; } +static int kernel_task_pid_alive(lua_State *L) +{ + struct task_struct *task = totask(L, 1); + lua_pushboolean(L, pid_alive(task)); + return 1; +} + static int meth_task_tostring(lua_State *L) { struct task_struct *task = totask(L, 1); @@ -335,6 +342,7 @@ static const luaL_Reg task_meth[] = { { "cmdline", kernel_task_cmdline }, { "capable", kernel_task_capable }, { "is_descendant", kernel_task_is_descendant }, + { "pid_alive", kernel_task_pid_alive }, { "__tostring", meth_task_tostring }, { NULL, NULL } }; From b28c709e30fd9d4b58971bcfdf26434e137aa988 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Fri, 16 Jan 2026 17:04:08 +0800 Subject: [PATCH 074/110] lua-lsm: simplify module environment for userdata --- security/lua/auxlib.c | 6 ------ security/lua/auxlib.h | 2 -- security/lua/lsm.c | 2 +- security/lua/lsm_defs.h | 4 +++- security/lua/lua_capability.c | 7 ------- security/lua/lua_fs.c | 11 ----------- security/lua/lua_kernel.c | 11 ----------- security/lua/lua_net.c | 6 ------ security/lua/lua_object.h | 12 +++++++++++- 9 files changed, 15 insertions(+), 46 deletions(-) diff --git a/security/lua/auxlib.c b/security/lua/auxlib.c index 23e414a3834b..96c951905566 100644 --- a/security/lua/auxlib.c +++ b/security/lua/auxlib.c @@ -195,12 +195,6 @@ void luaL_requiref(lua_State *L, const char *modname, } } -void settopfenvfrom(lua_State *L, int from) -{ - lua_getfenv(L, from); - lua_setfenv(L, -2); -} - unsigned int tocflags(lua_State *L, int idx, int top, const struct cflag_opt *opts, unsigned int d) { diff --git a/security/lua/auxlib.h b/security/lua/auxlib.h index 429df31d1f8e..2676283e6fa9 100644 --- a/security/lua/auxlib.h +++ b/security/lua/auxlib.h @@ -44,8 +44,6 @@ int lua_pcall_wrap(lua_State *L, int nargs, int nresults, int errfunc); void luaL_requiref(lua_State *L, const char *modname, lua_CFunction openf, int glb); -void settopfenvfrom(lua_State *L, int from); - struct cflag_opt { const char *name; unsigned int flag; diff --git a/security/lua/lsm.c b/security/lua/lsm.c index ba178b15a856..45a41b7f44d4 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -348,7 +348,7 @@ static int module_load(lua_State *L, struct lua_lsm_module *module) lua_pushlightuserdata(L, MODULE_KEY); lua_pushlightuserdata(L, module); lua_settable(L, -3); /* env.MODULE_KEY = module */ - *newtask(L) = current; + *newtask_nomain(L) = current; lua_setfield(L, -2, "current"); /* env.current = current */ lua_newtable(L); /* shared table */ diff --git a/security/lua/lsm_defs.h b/security/lua/lsm_defs.h index dd81bfa939ae..5cac41da9758 100644 --- a/security/lua/lsm_defs.h +++ b/security/lua/lsm_defs.h @@ -146,7 +146,7 @@ if (lua_isfunction(L, -1)) { \ int top = lua_gettop(L); \ lua_getfenv(L, -1); \ - lua_setfenv(L, -6); /* thread.fenv = lfunc.fenv */ \ + lua_setfield(L, LUA_REGISTRYINDEX, CURR_ENV); \ ASSIGN_FROM_FUNC_ ## vmtype(ret) \ __lua_lsm_vm_ ## NAME(L __VA_OPT__(,) \ __MAP(x, __SC_ARGS, __VA_ARGS__)); \ @@ -157,6 +157,8 @@ lua_pop(L, 1); /* pop _M */ \ RET_CHECK_ ## rettype(NAME, ret); \ } \ + lua_pushnil(L); \ + lua_setfield(L, LUA_REGISTRYINDEX, CURR_ENV); \ lua_pop(L, 4); \ lvm_put(L); \ return ret; \ diff --git a/security/lua/lua_capability.c b/security/lua/lua_capability.c index d2c547bb4483..22064c92d79f 100644 --- a/security/lua/lua_capability.c +++ b/security/lua/lua_capability.c @@ -65,13 +65,11 @@ static int capability_add(lua_State *L) if (tt == LUA_TUSERDATA) { const kernel_cap_t cap2 = tocap(L, 2); *newcap(L) = cap_combine(cap1, cap2); - settopfenvfrom(L, 1); return 1; } else { int cap = arg2cap(L, 2); cap_raise(cap1, cap); *newcap(L) = cap1; - settopfenvfrom(L, 1); return 1; } } @@ -83,13 +81,11 @@ static int capability_sub(lua_State *L) if (tt == LUA_TUSERDATA) { const kernel_cap_t cap2 = tocap(L, 2); *newcap(L) = cap_drop(cap1, cap2); - settopfenvfrom(L, 1); return 1; } else { int cap = arg2cap(L, 2); cap_lower(cap1, cap); *newcap(L) = cap1; - settopfenvfrom(L, 1); return 1; } } @@ -99,7 +95,6 @@ static int capability_mul(lua_State *L) const kernel_cap_t cap1 = tocap(L, 1); const kernel_cap_t cap2 = tocap(L, 2); *newcap(L) = cap_intersect(cap1, cap2); - settopfenvfrom(L, 1); return 1; } @@ -143,14 +138,12 @@ static const luaL_Reg cap_meth[] = { static int capability_cap_empty(lua_State *L) { *newcap(L) = CAP_EMPTY_SET; - /* TODO: settopfenvfrom(L, 1); */ return 1; } static int capability_cap_full(lua_State *L) { *newcap(L) = CAP_FULL_SET; - /* TODO: settopfenvfrom(L, 1); */ return 1; } diff --git a/security/lua/lua_fs.c b/security/lua/lua_fs.c index 63ccf287e1ff..6b5479637ebb 100644 --- a/security/lua/lua_fs.c +++ b/security/lua/lua_fs.c @@ -67,7 +67,6 @@ static int fs_dentry_backing_inode(lua_State *L) { struct dentry *dentry = todentry(L, 1); *newinode(L) = d_backing_inode(dentry); - settopfenvfrom(L, 1); return 1; } @@ -242,7 +241,6 @@ static int fs_file_dentry(lua_State *L) { const struct file *file = tofile(L, 1); *newdentry(L) = file_dentry(file); - settopfenvfrom(L, 1); return 1; } @@ -250,7 +248,6 @@ static int fs_file_inode(lua_State *L) { struct file *file = tofile(L, 1); *newinode(L) = file_inode(file); - settopfenvfrom(L, 1); return 1; } @@ -352,8 +349,6 @@ static const luaL_Reg fs_file_gc_meth[] = { struct linux_binprm *bprm = tobinprm(L, 1); \ if (bprm->name) { \ *new ## type(L) = bprm->name; \ - /* setfenv(file, getfenv(binprm)) */ \ - settopfenvfrom(L, 1); \ return 1; \ } else { \ return 0; \ @@ -379,7 +374,6 @@ static int fs_path_vfsmount(lua_State *L) { struct path *path = topath(L, 1); *newvfsmount(L) = path->mnt; - settopfenvfrom(L, 1); return 1; } @@ -387,7 +381,6 @@ static int fs_path_dentry(lua_State *L) { struct path *path = topath(L, 1); *newdentry(L) = path->dentry; - settopfenvfrom(L, 1); return 1; } @@ -431,7 +424,6 @@ static int fs_superblock_root(lua_State *L) { struct super_block *sb = tosuperblock(L, 1); *newdentry(L) = sb->s_root; - settopfenvfrom(L, 1); return 1; } @@ -486,7 +478,6 @@ static int fs_vfsmount_superblock(lua_State *L) { struct vfsmount *mnt = tovfsmount(L, 1); *newsuperblock(L) = mnt->mnt_sb; - settopfenvfrom(L, 1); return 1; } @@ -494,7 +485,6 @@ static int fs_vfsmount_mntidmap(lua_State *L) { struct vfsmount *mnt = tovfsmount(L, 1); *newmntidmap(L) = mnt_idmap(mnt); - settopfenvfrom(L, 1); return 1; } @@ -543,7 +533,6 @@ static int fs_filp_open(lua_State *L) lua_pushinteger(L, PTR_ERR(*filp)); return 2; } - /* TODO: settopfenvfrom(L, 1); */ return 1; } diff --git a/security/lua/lua_kernel.c b/security/lua/lua_kernel.c index 075cdf464514..0ea50c2f180e 100644 --- a/security/lua/lua_kernel.c +++ b/security/lua/lua_kernel.c @@ -52,11 +52,8 @@ static int kernel_cred_cap_eip(lua_State *L) if (top == 1) { /* get caps */ *newcap(L) = cred->cap_effective; - settopfenvfrom(L, 1); *newcap(L) = cred->cap_inheritable; - settopfenvfrom(L, 1); *newcap(L) = cred->cap_permitted; - settopfenvfrom(L, 1); return 3; } /* set caps */ @@ -75,7 +72,6 @@ static int kernel_cred_cap_bset(lua_State *L) struct cred *cred = tocred(L, 1); if (lua_gettop(L) == 1) { *newcap(L) = cred->cap_bset; - settopfenvfrom(L, 1); return 1; } else { cred->cap_bset = tocap(L, 2); @@ -89,7 +85,6 @@ static int kernel_cred_cap_ambient(lua_State *L) struct cred *cred = tocred(L, 1); if (lua_gettop(L) == 1) { *newcap(L) = cred->cap_ambient; - settopfenvfrom(L, 1); return 1; } else { cred->cap_ambient = tocap(L, 2); @@ -155,7 +150,6 @@ static int kernel_task_cred(lua_State *L) { struct task_struct *task = totask(L, 1); *(const struct cred **)newcred(L) = get_task_cred(task); - settopfenvfrom(L, 1); return 1; } @@ -178,7 +172,6 @@ static int kernel_task_group_leader(lua_State *L) struct task_struct *task = totask(L, 1); if (!thread_group_leader(task)) { *newtask(L) = rcu_dereference(task->group_leader); - settopfenvfrom(L, 1); } else { lua_settop(L, 1); } @@ -229,7 +222,6 @@ static int kernel_task_exe_file(lua_State *L) if (exe_file == NULL) return 0; *newgcfile(L) = exe_file; - settopfenvfrom(L, 1); return 1; } @@ -460,9 +452,6 @@ static int kernel_task_from_pid(lua_State *L) if (task == NULL) return 0; *newgctask(L) = task; - /* FIXME - settopfenvfrom(L, 1); - */ return 1; } diff --git a/security/lua/lua_net.c b/security/lua/lua_net.c index 7e479f2dcf2a..0428f8f47ef1 100644 --- a/security/lua/lua_net.c +++ b/security/lua/lua_net.c @@ -48,7 +48,6 @@ static int net_sock_socket(lua_State *L) { struct sock *sk = tosock(L, 1); *newsocket(L) = sk->sk_socket; - settopfenvfrom(L, 1); return 1; } @@ -159,7 +158,6 @@ static int net_socket_sock(lua_State *L) { struct socket *sock = tosocket(L, 1); *newsock(L) = sock->sk; - settopfenvfrom(L, 1); return 1; } @@ -167,7 +165,6 @@ static int net_socket_inode(lua_State *L) { struct socket *sock = tosocket(L, 1); *newinode(L) = SOCK_INODE(sock); - settopfenvfrom(L, 1); return 1; } @@ -184,7 +181,6 @@ static int net_skb_sock(lua_State *L) { struct sk_buff *skb = toskb(L, 1); *newsock(L) = skb->sk; - settopfenvfrom(L, 1); return 1; } @@ -192,7 +188,6 @@ static int net_skb_full_sk(lua_State *L) { struct sk_buff *skb = toskb(L, 1); *newsock(L) = skb_to_full_sk(skb); - settopfenvfrom(L, 1); return 1; } @@ -318,7 +313,6 @@ static int net_sock_alloc(lua_State *L) *sockp = sock_alloc(); if (*sockp == NULL) return 0; - /* TODO: settopfenvfrom(L, 1); */ return 1; } diff --git a/security/lua/lua_object.h b/security/lua/lua_object.h index 90c164dcb28b..5cee13e817a0 100644 --- a/security/lua/lua_object.h +++ b/security/lua/lua_object.h @@ -12,12 +12,14 @@ #include "lsm.h" #include "kvcache.h" +#define CURR_ENV "_CURR_ENV" + #define METHOD_NAME(name) ("method." #name) #define METHOD_NAME_RAW(name) ("method." #name ".raw") #define METHOD_NAME_GC(name) ("method." #name ".gc") #define LUA_OBJECT_META_DEFINE(name, ctype, d, metaname) \ - static inline ctype *new ## name(lua_State *L) \ + static inline ctype *new ## name ## _nomain(lua_State *L) \ { \ ctype *p = (ctype *)lua_newuserdata(L, sizeof(ctype)); \ *p = d; \ @@ -25,6 +27,14 @@ lua_setmetatable(L, -2); \ return p; \ } \ + /* Calling from the Main chunk requires setting the correct fenv. */ \ + static inline ctype *new ## name(lua_State *L) \ + { \ + ctype *p = new ## name ## _nomain(L); \ + lua_getfield(L, LUA_REGISTRYINDEX, CURR_ENV); \ + lua_setfenv(L, -2); \ + return p; \ + } \ static inline ctype *to ## name ## p(lua_State *L, int idx) \ { \ return (ctype *)checkudata3(L, idx, metaname); \ From b9f6e75a10f7b9241b6ee383129c07d25813d39e Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Fri, 16 Jan 2026 17:46:02 +0800 Subject: [PATCH 075/110] lua-lsm: pretty show sockaddr --- security/lua/lua_net.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/security/lua/lua_net.c b/security/lua/lua_net.c index 0428f8f47ef1..94898f51b4c8 100644 --- a/security/lua/lua_net.c +++ b/security/lua/lua_net.c @@ -284,15 +284,21 @@ static int meth_sockaddr_tostring(lua_State *L) int l; switch (sa->sa_family) { case AF_INET: - l = snprintf(buffer, sizeof(buffer), "inet: %pISpc", sa); + l = snprintf(buffer, sizeof(buffer), "sa.inet: %pISpc", sa); lua_pushlstring(L, buffer, l); break; case AF_INET6: - l = snprintf(buffer, sizeof(buffer), "inet6: %pISpc", sa); + l = snprintf(buffer, sizeof(buffer), "sa.inet6: %pISpc", sa); lua_pushlstring(L, buffer, l); break; case AF_UNIX: - lua_pushfstring(L, "unix: %s", ((struct sockaddr_un *)sa)->sun_path); + lua_pushfstring(L, "sa.unix: %s", ((struct sockaddr_un *)sa)->sun_path); + break; + case AF_NETLINK: + lua_pushfstring(L, "sa.netlink: %d", ((struct sockaddr_nl *)sa)->nl_pid); + break; + default: + lua_pushfstring(L, "sa.%s", family_tostring(sa->sa_family) ?: "(unknown)"); break; } return 1; From dbb7b7f13f4b1f9c99b8015a6a180aea227216d9 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Mon, 19 Jan 2026 20:06:23 +0800 Subject: [PATCH 076/110] lua-lsm: Count with atomic64_t --- security/lua/kvcache.c | 20 ++++++++++---------- security/lua/lsm.c | 38 +++++++++++++++++++------------------- security/lua/lsm.h | 6 +++--- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/security/lua/kvcache.c b/security/lua/kvcache.c index a31bb6cfaf33..a4fd21c1ca91 100644 --- a/security/lua/kvcache.c +++ b/security/lua/kvcache.c @@ -20,27 +20,27 @@ #ifdef CONFIG_SECURITY_LUA_LSM_STATS -static atomic_t node_nalloc = ATOMIC_INIT(0); -static atomic_t node_nfree = ATOMIC_INIT(0); -static atomic_t node_nusage = ATOMIC_INIT(0); +static atomic64_t node_nalloc = ATOMIC_INIT(0); +static atomic64_t node_nfree = ATOMIC_INIT(0); +static atomic64_t node_nusage = ATOMIC_INIT(0); static void kvcache_stats_alloc(void) { - atomic_inc(&node_nalloc); - atomic_inc(&node_nusage); + atomic64_inc(&node_nalloc); + atomic64_inc(&node_nusage); } static void kvcache_stats_free(void) { - atomic_inc(&node_nfree); - atomic_dec(&node_nusage); + atomic64_inc(&node_nfree); + atomic64_dec(&node_nusage); } void kvcache_stats_show(struct seq_file *m) { - seq_printf(m, "kvcache.nalloc\t= %9d\n", atomic_read(&node_nalloc)); - seq_printf(m, "kvcache.nfree\t= %9d\n", atomic_read(&node_nfree)); - seq_printf(m, "kvcache.nusage\t= %9d\n", atomic_read(&node_nusage)); + seq_printf(m, "kvcache.nalloc\t= %12lld\n", atomic64_read(&node_nalloc)); + seq_printf(m, "kvcache.nfree\t= %12lld\n", atomic64_read(&node_nfree)); + seq_printf(m, "kvcache.nusage\t= %12lld\n", atomic64_read(&node_nusage)); } #else diff --git a/security/lua/lsm.c b/security/lua/lsm.c index 45a41b7f44d4..d96af167c1f9 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -81,9 +81,9 @@ static atomic_t vm_nusage = ATOMIC_INIT(0); static atomic_t vm_nalloc = ATOMIC_INIT(0); static atomic_t vm_nfree = ATOMIC_INIT(0); -static atomic_t mem_nalloc = ATOMIC_INIT(0); -static atomic_t mem_nrealloc = ATOMIC_INIT(0); -static atomic_t mem_nfree = ATOMIC_INIT(0); +static atomic64_t mem_nalloc = ATOMIC64_INIT(0); +static atomic64_t mem_nrealloc = ATOMIC64_INIT(0); +static atomic64_t mem_nfree = ATOMIC64_INIT(0); static atomic_t mem_total = ATOMIC_INIT(0); static atomic_t mem_minimum = ATOMIC_INIT(INT_MAX); static atomic_t mem_maximum = ATOMIC_INIT(0); @@ -107,16 +107,16 @@ static void lvm_stats_memalloc(struct lvm_state *lvm, void *ptr, int minimum, maximum, nbytes; if (nsize == 0) { - atomic_inc(&mem_nfree); - atomic_inc(&lvm->nfree); + atomic64_inc(&mem_nfree); + atomic64_inc(&lvm->nfree); atomic_sub((int)osize, &mem_total); } else { if (ptr) { - atomic_inc(&mem_nrealloc); - atomic_inc(&lvm->nrealloc); + atomic64_inc(&mem_nrealloc); + atomic64_inc(&lvm->nrealloc); } else { - atomic_inc(&mem_nalloc); - atomic_inc(&lvm->nalloc); + atomic64_inc(&mem_nalloc); + atomic64_inc(&lvm->nalloc); } atomic_add(nsize - osize, &mem_total); @@ -143,16 +143,16 @@ void lvm_stats_show(struct seq_file *m) int total = atomic_read(&mem_total); int average = nusage ? total / nusage : 0; - seq_printf(m, "lvm.nalloc\t= %9d\n", atomic_read(&vm_nalloc)); - seq_printf(m, "lvm.nfree\t= %9d\n", atomic_read(&vm_nfree)); - seq_printf(m, "lvm.nusage\t= %9d\n", nusage); - seq_printf(m, "lmem.nalloc\t= %9d\n", atomic_read(&mem_nalloc)); - seq_printf(m, "lmem.nrealloc\t= %9d\n", atomic_read(&mem_nrealloc)); - seq_printf(m, "lmem.nfree\t= %9d\n", atomic_read(&mem_nfree)); - seq_printf(m, "lmem.total\t= %9d\n", total); - seq_printf(m, "lmem.average\t= %9d\n", average); - seq_printf(m, "lmem.minimum\t= %9d\n", atomic_read(&mem_minimum)); - seq_printf(m, "lmem.maximum\t= %9d\n", atomic_read(&mem_maximum)); + seq_printf(m, "lvm.nalloc\t= %12d\n", atomic_read(&vm_nalloc)); + seq_printf(m, "lvm.nfree\t= %12d\n", atomic_read(&vm_nfree)); + seq_printf(m, "lvm.nusage\t= %12d\n", nusage); + seq_printf(m, "lmem.nalloc\t= %12lld\n", atomic64_read(&mem_nalloc)); + seq_printf(m, "lmem.nrealloc\t= %12lld\n", atomic64_read(&mem_nrealloc)); + seq_printf(m, "lmem.nfree\t= %12lld\n", atomic64_read(&mem_nfree)); + seq_printf(m, "lmem.total\t= %12d\n", total); + seq_printf(m, "lmem.average\t= %12d\n", average); + seq_printf(m, "lmem.minimum\t= %12d\n", atomic_read(&mem_minimum)); + seq_printf(m, "lmem.maximum\t= %12d\n", atomic_read(&mem_maximum)); } int lsm_funcs_show(struct seq_file *m, void *v) diff --git a/security/lua/lsm.h b/security/lua/lsm.h index 0ca761a86db7..6ac048c89ed9 100644 --- a/security/lua/lsm.h +++ b/security/lua/lsm.h @@ -115,9 +115,9 @@ struct lvm_state { lua_State *L; atomic_t refcount; #ifdef CONFIG_SECURITY_LUA_LSM_STATS - atomic_t nalloc; - atomic_t nrealloc; - atomic_t nfree; + atomic64_t nalloc; + atomic64_t nrealloc; + atomic64_t nfree; #endif }; From 2a66d0b16299984f89c0ae60772ef41639cb670e Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Tue, 10 Feb 2026 16:01:44 +0800 Subject: [PATCH 077/110] lua-lsm: return busy if task alloc_lock held --- security/lua/lua_kernel.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/security/lua/lua_kernel.c b/security/lua/lua_kernel.c index 0ea50c2f180e..71e844bdea85 100644 --- a/security/lua/lua_kernel.c +++ b/security/lua/lua_kernel.c @@ -218,7 +218,13 @@ static int kernel_task_is_idle(lua_State *L) static int kernel_task_exe_file(lua_State *L) { struct task_struct *task = totask(L, 1); - struct file *exe_file = get_task_exe_file(task); + struct file *exe_file; + if (spin_is_locked(&task->alloc_lock)) { + lua_pushnil(L); + lua_pushstring(L, "busy"); + return 2; + } + exe_file = get_task_exe_file(task); if (exe_file == NULL) return 0; *newgcfile(L) = exe_file; @@ -236,6 +242,11 @@ static int kernel_task_exepath(lua_State *L) return 0; file = get_mm_exe_file(mm); } else { + if (spin_is_locked(&task->alloc_lock)) { + lua_pushnil(L); + lua_pushstring(L, "busy"); + return 2; + } file = get_task_exe_file(task); } if (file) { From b22012612fb852f396997cbdc5d8b635709dced7 Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Mon, 26 Jan 2026 15:57:08 +0800 Subject: [PATCH 078/110] lua-lsm: add lvm pool support Signed-off-by: Zongyao Chen --- security/lua/lsm.c | 157 +++++++++++++++++++++++++++++++++++++--- security/lua/lsm.h | 3 +- security/lua/lsm_defs.h | 3 + 3 files changed, 153 insertions(+), 10 deletions(-) diff --git a/security/lua/lsm.c b/security/lua/lsm.c index d96af167c1f9..26fdb8276b5a 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -199,11 +199,126 @@ static inline void lvm_stats_memalloc(struct lvm_state *lvm, void *ptr, static DEFINE_PER_CPU(struct lvm_state *, irq_lvms); +static int lua_state_alloc(struct lvm_state *lvm); +static void lua_state_free(struct lvm_state *lvm); + +#define LVM_POOL_MAX 8 + +struct lvm_pool_cpu { + struct lvm_state *head; + unsigned int count; + raw_spinlock_t lock; +}; + +static DEFINE_PER_CPU(struct lvm_pool_cpu, lvm_pools); + +static void lvm_pool_init_cpu(int cpu) +{ + struct lvm_pool_cpu *pool = &per_cpu(lvm_pools, cpu); + + pool->head = NULL; + pool->count = 0; + raw_spin_lock_init(&pool->lock); +} + +static struct lvm_state *lvm_pool_get(void) +{ + struct lvm_pool_cpu *pool; + struct lvm_state *lvm = NULL; + unsigned long flags; + int cpu; + + cpu = get_cpu(); + pool = &per_cpu(lvm_pools, cpu); + raw_spin_lock_irqsave(&pool->lock, flags); + if (pool->head) { + lvm = pool->head; + pool->head = lvm->next; + pool->count--; + } + raw_spin_unlock_irqrestore(&pool->lock, flags); + put_cpu(); + + if (lvm) + lvm->next = NULL; + + return lvm; +} + +static void lvm_pool_put(struct lvm_state *lvm) +{ + struct lvm_pool_cpu *pool; + unsigned long flags; + int cpu; + + if (!lvm) + return; + + cpu = get_cpu(); + pool = &per_cpu(lvm_pools, cpu); + raw_spin_lock_irqsave(&pool->lock, flags); + if (pool->count < LVM_POOL_MAX) { + lvm->next = pool->head; + pool->head = lvm; + pool->count++; + lvm = NULL; + } + raw_spin_unlock_irqrestore(&pool->lock, flags); + put_cpu(); + + if (lvm) { + lua_state_free(lvm); + kfree(lvm); + } +} + +static void lvm_vm_reset(struct lvm_state *lvm) +{ + struct lua_lsm_module *module; + lua_State *L = lvm->L; + int idx; + + if (!L) + return; + + lua_settop(L, 0); + lua_pushnil(L); + lua_setfield(L, LUA_REGISTRYINDEX, CURR_ENV); + + lua_getfield(L, LUA_REGISTRYINDEX, "_MODULES"); + if (lua_istable(L, -1)) { + idx = srcu_read_lock(&modules_ss); + list_for_each_entry_srcu(module, &lsm_modules, list, + srcu_read_lock_held(&modules_ss)) { + lua_pushstring(L, module->name); + lua_pushnil(L); + lua_rawset(L, -3); + } + srcu_read_unlock(&modules_ss, idx); + } + lua_pop(L, 1); + + lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); + if (lua_istable(L, -1)) { + idx = srcu_read_lock(&modules_ss); + list_for_each_entry_srcu(module, &lsm_modules, list, + srcu_read_lock_held(&modules_ss)) { + lua_pushstring(L, module->name); + lua_pushnil(L); + lua_rawset(L, -3); + } + srcu_read_unlock(&modules_ss, idx); + } + lua_pop(L, 1); + + lua_gc(L, LUA_GCCOLLECT, 0); +} + static lua_State * lvm_get_from_task(const struct task_struct *task, bool exclusive) { struct lua_lsm_task *llt = lua_lsm_task(task); - struct lvm_state *lvm = &llt->lvm; + struct lvm_state *lvm = llt->lvm; int n = refcount_acquire(&lvm->refcount); if (exclusive && n != 1) { refcount_release(&lvm->refcount); @@ -218,7 +333,7 @@ lvm_get_from_task(const struct task_struct *task, bool exclusive) static void lvm_put_to_task(const struct task_struct *task, lua_State *L) { struct lua_lsm_task *llt = lua_lsm_task(task); - struct lvm_state *lvm = &llt->lvm; + struct lvm_state *lvm = llt->lvm; int n = refcount_release(&lvm->refcount); KASSERT(n == 0, ("<%s> Lua VM is reused, refcount = %d\n", task->comm, n)); @@ -1068,15 +1183,30 @@ int modules_show(struct seq_file *m, void *v) int task_blob_init(struct task_struct *task) { struct lua_lsm_task *llt = lua_lsm_task(task); - struct lvm_state *lvm = &llt->lvm; + struct lvm_state *lvm; int err; kvcache_dict_init(&llt->dict); + lvm = lvm_pool_get(); + if (!lvm) { + lvm = kzalloc(sizeof(*lvm), GFP_KERNEL); + if (!lvm) + return -ENOMEM; + err = lua_state_alloc(lvm); + if (err) { + kfree(lvm); + return err; + } + } + refcount_init(&lvm->refcount, 0); - err = lua_state_alloc(lvm); - if (err) - return err; +#ifdef CONFIG_SECURITY_LUA_LSM_STATS + atomic64_set(&lvm->nalloc, 0); + atomic64_set(&lvm->nrealloc, 0); + atomic64_set(&lvm->nfree, 0); +#endif + llt->lvm = lvm; return 0; } @@ -1084,9 +1214,15 @@ int task_blob_init(struct task_struct *task) void task_blob_free(struct task_struct *task) { struct lua_lsm_task *llt = lua_lsm_task(task); - struct lvm_state *lvm = &llt->lvm; - lua_modules_free(task, lvm->L); - lua_state_free(lvm); + struct lvm_state *lvm = llt->lvm; + + if (lvm) { + lua_modules_free(task, lvm->L); + lvm_vm_reset(lvm); + refcount_init(&lvm->refcount, 0); + lvm_pool_put(lvm); + llt->lvm = NULL; + } kvcache_dict_free(&llt->dict); } @@ -1137,6 +1273,9 @@ static int __init lua_lsm_init(void) int cpu; int err; + for_each_possible_cpu(cpu) + lvm_pool_init_cpu(cpu); + err = task_blob_init(current); if (err) return err; diff --git a/security/lua/lsm.h b/security/lua/lsm.h index 6ac048c89ed9..fe75de318e1b 100644 --- a/security/lua/lsm.h +++ b/security/lua/lsm.h @@ -114,6 +114,7 @@ extern struct lsm_blob_sizes lua_lsm_blob_sizes; struct lvm_state { lua_State *L; atomic_t refcount; + struct lvm_state *next; #ifdef CONFIG_SECURITY_LUA_LSM_STATS atomic64_t nalloc; atomic64_t nrealloc; @@ -122,7 +123,7 @@ struct lvm_state { }; struct lua_lsm_task { - struct lvm_state lvm; + struct lvm_state *lvm; struct kvcache_dict dict; }; diff --git a/security/lua/lsm_defs.h b/security/lua/lsm_defs.h index 5cac41da9758..7ed42b22a3e9 100644 --- a/security/lua/lsm_defs.h +++ b/security/lua/lsm_defs.h @@ -9,6 +9,7 @@ #define _SECURITY_LUA_LSM_LSM_DEFS_H #include "lsm.h" +#include "lua_object.h" #define LSM_RET_DEFAULT(NAME) (NAME##_default) @@ -146,6 +147,8 @@ if (lua_isfunction(L, -1)) { \ int top = lua_gettop(L); \ lua_getfenv(L, -1); \ + *newtask_nomain(L) = current; \ + lua_setfield(L, -2, "current"); \ lua_setfield(L, LUA_REGISTRYINDEX, CURR_ENV); \ ASSIGN_FROM_FUNC_ ## vmtype(ret) \ __lua_lsm_vm_ ## NAME(L __VA_OPT__(,) \ From b2616990b23ad6bc867d49788ce47781966234d8 Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Thu, 12 Feb 2026 11:35:41 +0800 Subject: [PATCH 079/110] lua-lsm: reset pooled VM modules by table keys --- security/lua/lsm.c | 54 +++++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/security/lua/lsm.c b/security/lua/lsm.c index 26fdb8276b5a..8a839795e19d 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -274,9 +274,12 @@ static void lvm_pool_put(struct lvm_state *lvm) static void lvm_vm_reset(struct lvm_state *lvm) { - struct lua_lsm_module *module; lua_State *L = lvm->L; - int idx; + int modules_idx; + int keys_idx; + int loaded_idx; + int nkeys; + int i; if (!L) return; @@ -285,29 +288,40 @@ static void lvm_vm_reset(struct lvm_state *lvm) lua_pushnil(L); lua_setfield(L, LUA_REGISTRYINDEX, CURR_ENV); + /* Collect keys from _MODULES then clear _MODULES/_LOADED entries. */ lua_getfield(L, LUA_REGISTRYINDEX, "_MODULES"); if (lua_istable(L, -1)) { - idx = srcu_read_lock(&modules_ss); - list_for_each_entry_srcu(module, &lsm_modules, list, - srcu_read_lock_held(&modules_ss)) { - lua_pushstring(L, module->name); - lua_pushnil(L); - lua_rawset(L, -3); + modules_idx = lua_gettop(L); + lua_newtable(L); + keys_idx = lua_gettop(L); + nkeys = 0; + + lua_pushnil(L); + while (lua_next(L, modules_idx) != 0) { + lua_pushvalue(L, -2); + lua_rawseti(L, keys_idx, ++nkeys); + lua_pop(L, 1); } - srcu_read_unlock(&modules_ss, idx); - } - lua_pop(L, 1); - lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); - if (lua_istable(L, -1)) { - idx = srcu_read_lock(&modules_ss); - list_for_each_entry_srcu(module, &lsm_modules, list, - srcu_read_lock_held(&modules_ss)) { - lua_pushstring(L, module->name); - lua_pushnil(L); - lua_rawset(L, -3); + lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); + loaded_idx = lua_gettop(L); + for (i = 1; i <= nkeys; i++) { + lua_rawgeti(L, keys_idx, i); + if (!lua_isnil(L, -1)) { + lua_pushvalue(L, -1); + lua_pushnil(L); + lua_rawset(L, modules_idx); + + if (lua_istable(L, loaded_idx)) { + lua_pushvalue(L, -1); + lua_pushnil(L); + lua_rawset(L, loaded_idx); + } + } + lua_pop(L, 1); } - srcu_read_unlock(&modules_ss, idx); + lua_pop(L, 1); + lua_pop(L, 1); } lua_pop(L, 1); From 4936ad11348ad5824aaa9693eb9853abde9c450c Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Thu, 12 Feb 2026 17:59:35 +0800 Subject: [PATCH 080/110] lua-lsm: add lua-lsm documentation Signed-off-by: Zongyao Chen --- security/lua/docs/API.md | 93 ++++++++++++++++++++++++++++++ security/lua/docs/OBSERVABILITY.md | 45 +++++++++++++++ security/lua/docs/README.md | 79 +++++++++++++++++++++++++ security/lua/docs/USAGE.md | 50 ++++++++++++++++ 4 files changed, 267 insertions(+) create mode 100644 security/lua/docs/API.md create mode 100644 security/lua/docs/OBSERVABILITY.md create mode 100644 security/lua/docs/README.md create mode 100644 security/lua/docs/USAGE.md diff --git a/security/lua/docs/API.md b/security/lua/docs/API.md new file mode 100644 index 000000000000..695ca56dd700 --- /dev/null +++ b/security/lua/docs/API.md @@ -0,0 +1,93 @@ +# API + +## Module format + +A module is a Lua chunk that returns a table with metadata and hook functions. + +Required fields: +- `name` (string) +- `author` (string) +- `description` (string) +- `license` (string) +- `version` (number) + +Hook functions are keyed by the LSM hook name (e.g. `file_open`). + +Example: + +```lua +local errno = require("errno") + +return { + name = "demo", + author = "example", + description = "Example module", + license = "GPL-2.0", + version = 1, + + file_open = function(file, cred) + local path = file:path() + if path and path:match("^/etc/shadow$") then + return false, errno.EPERM + end + return true + end, +} +``` + +## Hook return values (int-return hooks) + +- `nil` or no return: default value +- `true`: allow (0) +- `false`: deny (-EPERM) +- `false, errno`: deny (-errno) +- `nil, errno`: deny (-errno) + +Use `require("errno")` to access errno constants. + +## Listing hook names + +From Lua: + +```lua +local kernel = require("kernel") +local hooks = kernel.lsm_funcs() +``` + +From userspace (if stats enabled): + +``` +cat /sys/kernel/security/lua/lsm_funcs +``` + +## Built-in libraries + +Lua-LSM preloads these libraries (use `require()`): + +- `kernel`: task, cred, printk, time, and helper utilities +- `fs`: file/dentry/inode/path helpers +- `net`: socket and address helpers +- `errno`: errno constants + `errname()` +- `capability`: kernel capability helpers +- `signal`: signal helpers + +## Common object methods (examples) + +Task (`task`): +- `task:pids()` -> pid, tgid +- `task:comm()` -> comm string +- `task:cred()` -> cred object + +File (`file`): +- `file:path()` -> path string or nil, err +- `file:inode()` -> inode object + +Inode (`inode`): +- `inode:ino()` -> inode number +- `inode:mode()` -> table or boolean checks +- `inode:ids()` -> uid, gid + +Dentry (`dentry`): +- `dentry:path()` -> path string + +For a full list, see the method tables in `lua_kernel.c` and `lua_fs.c`. diff --git a/security/lua/docs/OBSERVABILITY.md b/security/lua/docs/OBSERVABILITY.md new file mode 100644 index 000000000000..17efe805d56f --- /dev/null +++ b/security/lua/docs/OBSERVABILITY.md @@ -0,0 +1,45 @@ +# Observability + +## Debug logging + +Enable with `CONFIG_SECURITY_LUA_LSM_DEBUG`. Debug logs are on by default and +can be disabled with the kernel cmdline `lua.nodebug`. + +Logs are tagged with the current task and function name (see `debug.h`). + +## Stats (CONFIG_SECURITY_LUA_LSM_STATS) + +### VM + memory stats + +``` +cat /sys/kernel/security/lua/stats +``` + +Fields: +- `lvm.nalloc` / `lvm.nfree` / `lvm.nusage` +- `lmem.nalloc` / `lmem.nrealloc` / `lmem.nfree` +- `lmem.total` / `lmem.average` / `lmem.minimum` / `lmem.maximum` +- `kvcache.nalloc` / `kvcache.nfree` / `kvcache.nusage` + +### Hook timing stats + +``` +cat /sys/kernel/security/lua/lsm_funcs +``` + +Output columns: +- `name`: LSM hook name +- `nlsm`: number of modules implementing the hook +- `count`: invocation count +- `total`: total time (ns) +- `average`: average time (ns) +- `maxtime`: max time (ns) + +## Module list + +``` +cat /sys/kernel/security/lua/modules +``` + +Shows: name, license, size, number of hooks, load count, shdict count, +kvnode count, and author. diff --git a/security/lua/docs/README.md b/security/lua/docs/README.md new file mode 100644 index 000000000000..d8f183ef357d --- /dev/null +++ b/security/lua/docs/README.md @@ -0,0 +1,79 @@ +# Lua-LSM + +Scriptable operating systems with Lua. + +Lua-LSM runs LSM hooks inside an embedded Lua VM so you can ship, load, and +update security policy at runtime via securityfs. + +```lua +local logo = [==[ + __ __ ____ +/\ \ /\ \ /\ _`\ /'\_/`\ +\ \ \ __ __ __ \ \ \ \ \,\L\_\/\ \ + \ \ \ __/\ \/\ \ /'__`\ _______\ \ \ __\/_\__ \\ \ \__\ \ + \ \ \L\ \ \ \_\ \/\ \L\.\_/\______\\ \ \L\ \ /\ \L\ \ \ \_/\ \ + \ \____/\ \____/\ \__/\.\_\/______/ \ \____/ \ `\____\ \_\\ \_\ + \/___/ \/___/ \/__/\/_/ \/___/ \/_____/\/_/ \/_/ +]==] +``` + + +## Quick start + +1) Enable kernel config options: + +- `CONFIG_SECURITY_LUA_LSM=y` +- Optional: `CONFIG_SECURITY_LUA_LSM_STATS=y` +- Optional: `CONFIG_SECURITY_LUA_LSM_DEBUG=y` + +2) Ensure `lua` is in the active LSM list (CONFIG_LSM or `lsm=` on the kernel + command line). See `Documentation/security/lsm.rst` for details. + +3) Mount securityfs if it is not already mounted: + +``` +mount -t securityfs securityfs /sys/kernel/security +``` + +4) Load a module: + +``` +cat demo.lua > /sys/kernel/security/lua/register +``` + +5) Verify and unload: + +``` +cat /sys/kernel/security/lua/modules +echo demo > /sys/kernel/security/lua/unregister +``` + +## Example module + +```lua +local errno = require("errno") + +local M = { + name = "demo", + author = "example", + description = "Deny reads of /etc/shadow", + license = "GPL-2.0", + version = 1, +} + +function M.file_open(file, cred) + local path = file:path() + if path and path:match("^/etc/shadow$") then + return false, errno.EPERM + end + return true +end + +return M +``` + +## Docs + +- `USAGE.md` - build/enable and runtime management +- `API.md` - module format, hook returns, and core APIs +- `OBSERVABILITY.md` - stats, debug logs, and troubleshooting data diff --git a/security/lua/docs/USAGE.md b/security/lua/docs/USAGE.md new file mode 100644 index 000000000000..5c22c31d1155 --- /dev/null +++ b/security/lua/docs/USAGE.md @@ -0,0 +1,50 @@ +# Usage + +## Build and enable + +- Enable `CONFIG_SECURITY_LUA_LSM`. +- Include `lua` in the LSM order (CONFIG_LSM or kernel cmdline `lsm=`). +- Optional: + - `CONFIG_SECURITY_LUA_LSM_STATS` for stats files + - `CONFIG_SECURITY_LUA_LSM_DEBUG` for debug logging + +## Runtime control (securityfs) + +Mount securityfs (if needed): + +``` +mount -t securityfs securityfs /sys/kernel/security +``` + +Load a module: + +``` +cat policy.lua > /sys/kernel/security/lua/register +``` + +Unload a module: + +``` +echo policy > /sys/kernel/security/lua/unregister +``` + +List modules: + +``` +cat /sys/kernel/security/lua/modules +``` + +Check version: + +``` +cat /sys/kernel/security/lua/version +``` + +## Permissions + +Loading and unloading modules requires `CAP_MAC_ADMIN`. + +## Error handling + +- Register/unregister return negative errno on failure. +- Hook return values map to LSM errors; see `docs/API.md`. From fd30df422265f92bb7aacdcd566465aaac821581 Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Thu, 26 Feb 2026 11:38:51 +0800 Subject: [PATCH 081/110] lua-lsm: add bdev_setintegrity payload handling Signed-off-by: Zongyao Chen --- security/lua/lsm_defs.c | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/security/lua/lsm_defs.c b/security/lua/lsm_defs.c index 539ff06e35c0..b7665457feab 100644 --- a/security/lua/lsm_defs.c +++ b/security/lua/lsm_defs.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include /* for __MAP */ @@ -3507,7 +3508,7 @@ LUA_LSM_VOID_DEFINE1(bdev_free_security, struct block_device *, bdev) } /** - * TODO: bdev_setintegrity + * bdev_setintegrity * Default: 0 */ LUA_LSM_INT_DEFINE4(bdev_setintegrity, struct block_device *, bdev, @@ -3515,8 +3516,31 @@ LUA_LSM_INT_DEFINE4(bdev_setintegrity, struct block_device *, bdev, const void *, value, size_t, size) { *newbdev(L) = bdev; - lua_pushnil(L); /* TODO: type */ - lua_pushnil(L); /* TODO: value */ - lua_pushnil(L); /* TODO: size */ -} + lua_pushinteger(L, (lua_Integer)type); + + if (!value || size == 0) { + lua_pushnil(L); + lua_pushinteger(L, (lua_Integer)size); + return; + } + if (type == LSM_INT_DMVERITY_ROOTHASH) { + const struct dm_verity_digest *d = value; + lua_createtable(L, 0, 3); + if (d->alg) { + lua_pushstring(L, d->alg); + lua_setfield(L, -2, "alg"); + } + if (d->digest && d->digest_len) { + lua_pushlstring(L, d->digest, d->digest_len); + lua_setfield(L, -2, "digest"); + } + lua_pushinteger(L, (lua_Integer)d->digest_len); + lua_setfield(L, -2, "digest_len"); + lua_pushinteger(L, (lua_Integer)d->digest_len); + return; + } + + lua_pushlstring(L, value, size); + lua_pushinteger(L, (lua_Integer)size); +} From c12d17c5152a438d19e6db781897056fd3fd8f5b Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Tue, 24 Mar 2026 17:26:54 +0800 Subject: [PATCH 082/110] lua-lsm: Port from 6.17 to 6.19 --- security/lua/lsm.c | 3 ++- security/lua/lsm.h | 3 +++ security/lua/lsm_defs.c | 8 +++----- security/lua/lua_fs.c | 5 ++--- security/lua/securityfs.c | 3 +-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/security/lua/lsm.c b/security/lua/lsm.c index 8a839795e19d..ac5c7859f433 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -1316,8 +1316,9 @@ static int __init lua_lsm_init(void) } DEFINE_LSM(lua) = { - .name = "lua", + .id = &lua_lsmid, .enabled = &lua_lsm_enabled, .blobs = &lua_lsm_blob_sizes, .init = lua_lsm_init, + .initcall_fs = lua_lsm_securityfs_init, }; diff --git a/security/lua/lsm.h b/security/lua/lsm.h index fe75de318e1b..0f3bd3bd2941 100644 --- a/security/lua/lsm.h +++ b/security/lua/lsm.h @@ -217,4 +217,7 @@ int luaopen_errno(lua_State *L); int luaopen_capability(lua_State *L); int luaopen_signal(lua_State *L); +/* securityfs interface */ +int lua_lsm_securityfs_init(void); + #endif /* ! _SECURITY_LUA_LSM_LSM_H */ diff --git a/security/lua/lsm_defs.c b/security/lua/lsm_defs.c index b7665457feab..b2f38ece7086 100644 --- a/security/lua/lsm_defs.c +++ b/security/lua/lsm_defs.c @@ -548,7 +548,7 @@ LUA_LSM_INT_DEFINE5(dentry_init_security, struct dentry *, dentry, * Default: 0 */ LUA_LSM_INT_DEFINE5(dentry_create_files_as, struct dentry *, dentry, - int, mode, struct qstr *, name, + int, mode, const struct qstr *, name, const struct cred *, old, struct cred *, new) { *newdentry(L) = dentry; @@ -1590,8 +1590,7 @@ LUA_LSM_INT_DEFINE1(file_truncate, struct file *, file) /** * task_alloc - prepare */ -LUA_LSM_PREPARE_DEFINE2(task_alloc, struct task_struct *, task, - unsigned long, clone_flags) +LUA_LSM_PREPARE_DEFINE2(task_alloc, struct task_struct *, task, u64, clone_flags) { return task_blob_init(task); } @@ -1600,8 +1599,7 @@ LUA_LSM_PREPARE_DEFINE2(task_alloc, struct task_struct *, task, * task_alloc * Default: 0 */ -LUA_LSM_INT_DEFINE2(task_alloc, struct task_struct *, task, - unsigned long, clone_flags) +LUA_LSM_INT_DEFINE2(task_alloc, struct task_struct *, task, u64, clone_flags) { *newtask(L) = task; lua_pushnumber(L, (lua_Number)clone_flags); diff --git a/security/lua/lua_fs.c b/security/lua/lua_fs.c index 6b5479637ebb..0460867a1ad0 100644 --- a/security/lua/lua_fs.c +++ b/security/lua/lua_fs.c @@ -460,9 +460,8 @@ static int fs_fscontext_parse_fs_string(lua_State *L) { struct fs_context *fc = tofscontext(L, 1); const char *key = luaL_checkstring(L, 2); - size_t size; - const char *value = luaL_checklstring(L, 3, &size); - int rc = vfs_parse_fs_string(fc, key, value, size); + const char *value = luaL_checkstring(L, 3); + int rc = vfs_parse_fs_string(fc, key, value); lua_pushboolean(L, rc == 0); return 1; } diff --git a/security/lua/securityfs.c b/security/lua/securityfs.c index 21947c8b5c12..160c40b6d9ea 100644 --- a/security/lua/securityfs.c +++ b/security/lua/securityfs.c @@ -171,7 +171,7 @@ static struct lua_lsm_file { { NULL, 0, NULL } }; -static int __init lua_lsm_securityfs_init(void) +int __init lua_lsm_securityfs_init(void) { struct dentry *dir; struct dentry *dentry; @@ -202,4 +202,3 @@ static int __init lua_lsm_securityfs_init(void) return 0; } -fs_initcall(lua_lsm_securityfs_init); From b593b908d6f4935ebff5f1e2090f936c7ce02b39 Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Thu, 26 Mar 2026 10:26:44 +0800 Subject: [PATCH 083/110] lua-lsm: style cleanups Signed-off-by: Zongyao Chen Discussed-in: Merge-Request 26537804 1/3, URL: https://code.alibaba-inc.com/opensrc/linux/codereview/26537804 --- security/lua/auxlib.c | 79 ++++++++------ security/lua/auxlib.h | 19 ++-- security/lua/kvcache.c | 56 +++++----- security/lua/kvcache.h | 1 + security/lua/lsm.c | 135 ++++++++++++------------ security/lua/lsm.h | 9 +- security/lua/lua_capability.c | 29 ++++-- security/lua/lua_errno.c | 1 + security/lua/lua_fs.c | 43 ++++++-- security/lua/lua_kernel.c | 63 ++++++++---- security/lua/lua_net.c | 187 +++++++++++++++++++++++++--------- security/lua/refcount.h | 2 +- security/lua/securityfs.c | 7 +- 13 files changed, 415 insertions(+), 216 deletions(-) diff --git a/security/lua/auxlib.c b/security/lua/auxlib.c index 96c951905566..f39389a98e42 100644 --- a/security/lua/auxlib.c +++ b/security/lua/auxlib.c @@ -21,20 +21,20 @@ #include "auxlib.h" #include "lua_object.h" - void lua_table_dump(lua_State *L, int idx, int level, int max) { int i = 1; + if (idx < 0) idx = lua_gettop(L) + 1 + idx; if (!lua_istable(L, idx)) { pr_err("%*s \n", - level * 4, "", luaL_typename(L, -1)); + level * 4, "", luaL_typename(L, -1)); return; } if (level > 10) { pr_err("%*s
\n", - level * 4, "", level); + level * 4, "", level); return; } /* table is in the stack at index 't' */ @@ -95,7 +95,7 @@ void lua_stack_dump(lua_State *L) int lua_traceback(lua_State *L) { pr_err("@_@ LuaVM traceback: %s [%d] Lua stacktop = %d\n", - current->comm, task_pid_nr(current), lua_gettop(L)); + current->comm, task_pid_nr(current), lua_gettop(L)); if (!lua_isstring(L, 1)) /* 'message' not a string? */ return 1; /* keep it intact */ pr_err("@_@ LuaVM: %s\n", lua_tostring(L, -1)); @@ -124,7 +124,7 @@ int lua_traceback(lua_State *L) } int luaL_loadbuffer_wrap(lua_State *L, const char *buff, - size_t sz, const char *name) + size_t sz, const char *name) { int status; @@ -132,12 +132,19 @@ int luaL_loadbuffer_wrap(lua_State *L, const char *buff, if (status != 0) { const char * __maybe_unused error = lua_tostring(L, -1); int err; + __log_err("load: status = %d, top = %d, %s\n", - status, lua_gettop(L), error); + status, lua_gettop(L), error); switch (status) { - case LUA_ERRMEM: err = -ENOMEM; break; - case LUA_ERRSYNTAX: err = -EDOM; break; - default: err = -EINVAL; break; + case LUA_ERRMEM: + err = -ENOMEM; + break; + case LUA_ERRSYNTAX: + err = -EDOM; + break; + default: + err = -EINVAL; + break; } lua_pop(L, 1); return err; @@ -153,15 +160,24 @@ int lua_pcall_wrap(lua_State *L, int nargs, int nresults, int errfunc) if (status != 0) { int err; const char *error = lua_tostring(L, -1); + if (!error) error = "unknown error"; __log_err("pcall: status = %d, top = %d [%s]\n\t%s\n", - status, lua_gettop(L), luaL_typename(L, -1), error); + status, lua_gettop(L), luaL_typename(L, -1), error); switch (status) { - case LUA_ERRMEM: err = -ENOMEM; break; - case LUA_ERRRUN: err = -ENOEXEC; break; - case LUA_ERRERR: err = -EFAULT; break; - default: err = -EINVAL; break; + case LUA_ERRMEM: + err = -ENOMEM; + break; + case LUA_ERRRUN: + err = -ENOEXEC; + break; + case LUA_ERRERR: + err = -EFAULT; + break; + default: + err = -EINVAL; + break; } lua_pop(L, 1); return err; @@ -176,7 +192,7 @@ int lua_pcall_wrap(lua_State *L, int nargs, int nresults, int errfunc) ** Leaves resulting module on the top. */ void luaL_requiref(lua_State *L, const char *modname, - lua_CFunction openf, int glb) + lua_CFunction openf, int glb) { luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 1); lua_getfield(L, -1, modname); /* _LOADED[modname] */ @@ -196,7 +212,7 @@ void luaL_requiref(lua_State *L, const char *modname, } unsigned int tocflags(lua_State *L, int idx, int top, - const struct cflag_opt *opts, unsigned int d) + const struct cflag_opt *opts, unsigned int d) { unsigned int flags = 0; const char *s; @@ -241,7 +257,6 @@ unsigned int tocflags(lua_State *L, int idx, int top, case LUA_TBOOLEAN: if (lua_toboolean(L, idx)) break; - /* else fall through */ fallthrough; case LUA_TNONE: case LUA_TNIL: @@ -257,6 +272,7 @@ const char * fromcflags(const struct cflag_opt *opts, unsigned int flag, const char *d) { int i; + for (i = 0; opts[i].name; i++) { if (opts[i].flag == flag) return opts[i].name; @@ -265,10 +281,11 @@ fromcflags(const struct cflag_opt *opts, unsigned int flag, const char *d) } void table_fromopts(lua_State *L, const struct cflag_opt *opts, - unsigned int bitfield, unsigned int mask) + unsigned int bitfield, unsigned int mask) { int i; int hw; + if (!bitfield) { for (i = 0; opts[i].name; i++) bitfield |= opts[i].flag; @@ -316,7 +333,8 @@ void createmeta(lua_State *L, const char *tname, const char *name, void *checkudata(lua_State *L, int ud, const char *name) { void *p = lua_touserdata(L, ud); - if (p != NULL) { /* value is a userdata? */ + + if (p) { /* value is a userdata? */ if (lua_getmetatable(L, ud)) { /* does it have a metatable? */ lua_getfield(L, LUA_REGISTRYINDEX, name); /* get correct metatable */ if (lua_rawequal(L, -1, -2)) { /* does it have the correct mt? */ @@ -333,9 +351,9 @@ void *checkudata(lua_State *L, int ud, const char *name) * [ [ gc ] ------> regular ] ------> raw */ void createmeta3(lua_State *L, const char *name, const luaL_Reg *base, - const char *tname_gc, const luaL_Reg *funcs_gc, - const char *tname, const luaL_Reg *funcs, - const char *tname_raw, const luaL_Reg *funcs_raw) + const char *tname_gc, const luaL_Reg *funcs_gc, + const char *tname, const luaL_Reg *funcs, + const char *tname_raw, const luaL_Reg *funcs_raw) { if (funcs_gc && funcs) createmeta(L, tname_gc, name, funcs_gc, base, 0); @@ -355,7 +373,8 @@ void *checkudata3(lua_State *L, int ud, const char *tname) { int idx, n; void *p = lua_touserdata(L, ud); - if (p == NULL) + + if (!p) return NULL; luaL_getmetatable(L, tname); @@ -395,7 +414,7 @@ int aux_file_path(lua_State *L, struct file *filp) return 2; } - if (unlikely(filp->f_path.dentry == NULL)) { + if (unlikely(!filp->f_path.dentry)) { lua_pushnil(L); lua_pushstring(L, errname(-ENOENT)); return 2; @@ -404,7 +423,7 @@ int aux_file_path(lua_State *L, struct file *filp) path = file_path(filp, buffer, sizeof(buffer)); if (PTR_ERR(path) == -ENAMETOOLONG) { buf = kmalloc(PATH_MAX, lua_lsm_gfp()); - if (buf == NULL) { + if (!buf) { lua_pushnil(L); lua_pushstring(L, errname(-ENOMEM)); return 2; @@ -419,8 +438,7 @@ int aux_file_path(lua_State *L, struct file *filp) lua_pushstring(L, path); nres = 1; } - if (buf) - kfree(buf); + kfree(buf); return nres; } @@ -437,7 +455,7 @@ int aux_dentry_path(lua_State *L, struct dentry *dentry, int rawpath) path = dentry_path(dentry, buffer, sizeof(buffer)); if (PTR_ERR(path) == -ENAMETOOLONG) { buf = kmalloc(PATH_MAX, lua_lsm_gfp()); - if (buf == NULL) { + if (!buf) { lua_pushnil(L); lua_pushstring(L, errname(-ENOMEM)); return 2; @@ -455,8 +473,7 @@ int aux_dentry_path(lua_State *L, struct dentry *dentry, int rawpath) lua_pushstring(L, path); nres = 1; } - if (buf) - kfree(buf); + kfree(buf); return nres; } @@ -508,6 +525,7 @@ int arg2cap(lua_State *L, int idx) }; int tt = lua_type(L, idx); int cap; + switch (tt) { case LUA_TNUMBER: cap = luaL_checkinteger(L, idx); @@ -549,6 +567,7 @@ int aux_capable(lua_State *L, const struct cred *cred, int idx) err = cap_capable(cred, current_user_ns(), cap, opt); } else { struct task_struct *task = totask(L, idx); + cap = arg2cap(L, idx + 1); if (top >= idx + 2) opt = tocflags(L, idx + 2, top, opts, CAP_OPT_NONE); diff --git a/security/lua/auxlib.h b/security/lua/auxlib.h index 2676283e6fa9..657785ebb964 100644 --- a/security/lua/auxlib.h +++ b/security/lua/auxlib.h @@ -19,8 +19,8 @@ static inline gfp_t lua_lsm_gfp(void) */ if (in_atomic() || rcu_preempt_depth() > 0) return GFP_ATOMIC; - else - return GFP_NOFS; + + return GFP_NOFS; } #define luaL_newlibtable(L, l) \ @@ -38,11 +38,11 @@ void lua_stack_dump(lua_State *L); int lua_traceback(lua_State *L); int luaL_loadbuffer_wrap(lua_State *L, const char *buff, - size_t sz, const char *name); + size_t sz, const char *name); int lua_pcall_wrap(lua_State *L, int nargs, int nresults, int errfunc); void luaL_requiref(lua_State *L, const char *modname, - lua_CFunction openf, int glb); + lua_CFunction openf, int glb); struct cflag_opt { const char *name; @@ -50,13 +50,13 @@ struct cflag_opt { }; unsigned int tocflags(lua_State *L, int idx, int top, - const struct cflag_opt *opts, unsigned int d); + const struct cflag_opt *opts, unsigned int d); const char * fromcflags(const struct cflag_opt *opts, unsigned int flag, const char *d); void table_fromopts(lua_State *L, const struct cflag_opt *opts, - unsigned int bitfield, unsigned int mask); + unsigned int bitfield, unsigned int mask); void **newcptr(lua_State *L, const char *metatable); void createmeta(lua_State *L, const char *tname, const char *name, @@ -64,12 +64,11 @@ void createmeta(lua_State *L, const char *tname, const char *name, void *checkudata(lua_State *L, int ud, const char *name); void createmeta3(lua_State *L, const char *name, const luaL_Reg *base, - const char *tname_gc, const luaL_Reg *funcs_gc, - const char *tname, const luaL_Reg *funcs, - const char *tname_raw, const luaL_Reg *funcs_raw); + const char *tname_gc, const luaL_Reg *funcs_gc, + const char *tname, const luaL_Reg *funcs, + const char *tname_raw, const luaL_Reg *funcs_raw); void *checkudata3(lua_State *L, int ud, const char *tname); - struct const_value { const char *name; intptr_t value; diff --git a/security/lua/kvcache.c b/security/lua/kvcache.c index a4fd21c1ca91..8ac8c9438a41 100644 --- a/security/lua/kvcache.c +++ b/security/lua/kvcache.c @@ -17,7 +17,6 @@ #include "lsm.h" #include "kvcache.h" - #ifdef CONFIG_SECURITY_LUA_LSM_STATS static atomic64_t node_nalloc = ATOMIC_INIT(0); @@ -53,12 +52,13 @@ static inline void kvcache_stats_free(void) {} static int kvcache_node_cmp(struct kvcache_node *n1, struct kvcache_node *n2) { int n = strcmp(n1->key, n2->key); + if (n != 0) return n; return (char *)n1->module - (char *)n2->module; } -RB_GENERATE_STATIC(kvcache, kvcache_node, node, kvcache_node_cmp); +RB_GENERATE_STATIC(kvcache, kvcache_node, node, kvcache_node_cmp); static int kvcache_result(lua_State *L, int err) { @@ -69,14 +69,14 @@ static int kvcache_result(lua_State *L, int err) static struct kvcache_node * kvcache_node_alloc(struct kvcache_dict *dict, struct lua_lsm_module *module, - const char *key, size_t len) + const char *key, size_t len) { size_t l = sizeof(struct kvcache_node); struct kvcache_node *node; l += key ? (len + 1) : 0; node = kmalloc(l, lua_lsm_gfp()); - if (node == NULL) + if (!node) return NULL; if (key) { @@ -121,7 +121,7 @@ static void kvcache_node_hold(struct kvcache_node *node) static void kvcache_node_drop(struct kvcache_node *node) { - if (node == NULL) + if (!node) return; if (refcount_release(&node->refcount) == 0) kvcache_node_free(node); @@ -129,10 +129,11 @@ static void kvcache_node_drop(struct kvcache_node *node) static struct kvcache_node * kvcache_lookup(struct kvcache_dict *dict, - struct lua_lsm_module *module, const char *key) + struct lua_lsm_module *module, const char *key) { struct kvcache_node tmp, *node; unsigned long flags; + tmp.key = key; tmp.module = module; read_lock_irqsave(&dict->lock, flags); @@ -144,7 +145,7 @@ kvcache_lookup(struct kvcache_dict *dict, static struct kvcache_node * kvcache_module_link(struct kvcache_dict *dict, - struct lua_lsm_module *module, struct kvcache_node *node) + struct lua_lsm_module *module, struct kvcache_node *node) { struct kvcache_node *prev; unsigned long flags; @@ -177,7 +178,8 @@ kvcache_module_link(struct kvcache_dict *dict, static void kvcache_module_unlink_unlocked(struct kvcache_dict *dict, - struct lua_lsm_module *module, struct kvcache_node *node) + struct lua_lsm_module *module, + struct kvcache_node *node) { RB_REMOVE(kvcache, &dict->root, node); atomic_dec(&dict->count); @@ -191,9 +193,11 @@ kvcache_module_unlink_unlocked(struct kvcache_dict *dict, static void kvcache_module_unlink(struct kvcache_dict *dict, - struct lua_lsm_module *module, struct kvcache_node *node) + struct lua_lsm_module *module, + struct kvcache_node *node) { unsigned long flags; + write_lock_irqsave(&dict->lock, flags); kvcache_module_unlink_unlocked(dict, module, node); write_unlock_irqrestore(&dict->lock, flags); @@ -257,7 +261,7 @@ static int kvcache_node_refill(lua_State *L, int idx, struct kvcache_node *node) } static int kvcache_set(lua_State *L, struct kvcache_dict *dict, - struct lua_lsm_module *module) + struct lua_lsm_module *module) { size_t len; const char *key = luaL_checklstring(L, 2, &len); @@ -266,12 +270,12 @@ static int kvcache_set(lua_State *L, struct kvcache_dict *dict, int err; node = kvcache_lookup(dict, module, key); - if (node == NULL) { + if (!node) { if (tt == LUA_TNIL) goto ret; node = kvcache_node_alloc(dict, module, key, len); - if (node == NULL) + if (!node) return kvcache_result(L, -ENOMEM); err = kvcache_node_fill(L, 3, node); @@ -336,7 +340,7 @@ static int kvcache_node_get(lua_State *L, struct kvcache_node *node) } static int kvcache_get(lua_State *L, struct kvcache_dict *dict, - struct lua_lsm_module *module) + struct lua_lsm_module *module) { const char *key = luaL_checkstring(L, 2); struct kvcache_node *node; @@ -352,7 +356,7 @@ static int kvcache_get(lua_State *L, struct kvcache_dict *dict, } static int kvcache_incr(lua_State *L, struct kvcache_dict *dict, - struct lua_lsm_module *module) + struct lua_lsm_module *module) { size_t len; const char *key = luaL_checklstring(L, 2, &len); @@ -362,9 +366,9 @@ static int kvcache_incr(lua_State *L, struct kvcache_dict *dict, int err; node = kvcache_lookup(dict, module, key); - if (node == NULL) { + if (!node) { node = kvcache_node_alloc(dict, module, key, len); - if (node == NULL) + if (!node) return kvcache_result(L, -ENOMEM); node->n = n; @@ -417,6 +421,7 @@ int kvcache_module_nodes_gc(struct lua_lsm_module *module) spin_lock_bh(&nodes_gc_lock); list_for_each_entry(node, &module->kvnodes, modlist) { struct kvcache_dict *dict = node->dict; + BUG_ON(!dict); /* * module is set to NULL, so there is no need to remove @@ -435,7 +440,7 @@ int kvcache_module_nodes_gc(struct lua_lsm_module *module) } __log_info("module <%s>, kvnodes_count = %d, freed = %d\n", - module->name, count, n); + module->name, count, n); WARN_ON(count != n); return n; } @@ -485,14 +490,15 @@ static struct lua_lsm_module *module_from_object_fenv(lua_State *L, int idx) lua_gettable(L, -2); if (!lua_islightuserdata(L, -1)) { const char * __maybe_unused key = luaL_checkstring(L, 2); + if (luaL_callmeta(L, 1, "__tostring")) { __log_err("NO object.fenv: <%s> key = %s, top = %d [%s]\n", - lua_tostring(L, -1), key, lua_gettop(L), - luaL_typename(L, -1)); + lua_tostring(L, -1), key, lua_gettop(L), + luaL_typename(L, -1)); lua_pop(L, 3); } else { __log_err("NO object.fenv: key = %s, top = %d\n", - key, lua_gettop(L)); + key, lua_gettop(L)); lua_pop(L, 2); } return NULL; @@ -508,7 +514,7 @@ int lua_object_get(lua_State *L, struct kvcache_dict *dict) struct lua_lsm_module *module; module = module_from_object_fenv(L, 1); - if (module == NULL) + if (!module) return 0; return kvcache_get(L, dict, module); } @@ -518,7 +524,7 @@ int lua_object_incr(lua_State *L, struct kvcache_dict *dict) struct lua_lsm_module *module; module = module_from_object_fenv(L, 1); - if (module == NULL) + if (!module) return kvcache_result(L, -ESRCH); return kvcache_incr(L, dict, module); @@ -570,7 +576,7 @@ int lua_object_newindex(lua_State *L, struct kvcache_dict *dict) struct lua_lsm_module *module; module = module_from_object_fenv(L, 1); - if (module == NULL) + if (!module) return kvcache_result(L, -ESRCH); return kvcache_set(L, dict, module); @@ -581,18 +587,21 @@ int lua_object_newindex(lua_State *L, struct kvcache_dict *dict) static int shdict_set(lua_State *L) { struct kvcache_dict *shdict = toshdict(L, 1); + return kvcache_set(L, shdict, NULL); } static int shdict_get(lua_State *L) { struct kvcache_dict *shdict = toshdict(L, 1); + return kvcache_get(L, shdict, NULL); } static int shdict_incr(lua_State *L) { struct kvcache_dict *shdict = toshdict(L, 1); + return kvcache_incr(L, shdict, NULL); } @@ -617,6 +626,7 @@ static int shdict_tostring(lua_State *L) { struct kvcache_dict *shdict = toshdict(L, 1); unsigned long flags; + read_lock_irqsave(&shdict->lock, flags); lua_pushfstring(L, "shdict (%d / %d)", atomic_read(&shdict->count), shdict->capacity); diff --git a/security/lua/kvcache.h b/security/lua/kvcache.h index 85899905b2cb..85078f3e5e7e 100644 --- a/security/lua/kvcache.h +++ b/security/lua/kvcache.h @@ -40,6 +40,7 @@ struct kvcache_dict { int capacity; atomic_t count; rwlock_t lock; + RB_HEAD(kvcache, kvcache_node) root; }; diff --git a/security/lua/lsm.c b/security/lua/lsm.c index ac5c7859f433..b701ab6e5402 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -27,7 +27,6 @@ #include "lua_object.h" #include "lsm_defs.h" - #ifdef DEBUG bool debug = true; @@ -101,7 +100,7 @@ static void lvm_stats_vmfree(void) } static void lvm_stats_memalloc(struct lvm_state *lvm, void *ptr, - size_t osize, size_t nsize) + size_t osize, size_t nsize) { lua_State *L = lvm->L; int minimum, maximum, nbytes; @@ -122,7 +121,7 @@ static void lvm_stats_memalloc(struct lvm_state *lvm, void *ptr, atomic_add(nsize - osize, &mem_total); } - if (L == NULL) + if (!L) return; minimum = atomic_read(&mem_minimum); @@ -162,16 +161,17 @@ int lsm_funcs_show(struct seq_file *m, void *v) seq_printf(m, "stats for lua-lsm (ns)\n"); seq_printf(m, "%3s %-28s %4s %12s %15s %10s %12s\n", - "num", "name", "nlsm", "count", "total", "average", "maxtime"); + "num", "name", "nlsm", "count", "total", "average", "maxtime"); seq_printf(m, "%s\n", TABLINE); for (stat = lua_lsm_hook_stats; stat->name; stat++) { int n = atomic_read(&stat->count); s64 total = atomic64_read(&stat->time); s64 maxtime = atomic64_read(&stat->maxtime); + seq_printf(m, "%3d %-28s %4d %12d %15llu %10llu %12llu\n", - i++, stat->name, atomic_read(&stat->nhooks), - n, total, n ? total / n : 0, maxtime); + i++, stat->name, atomic_read(&stat->nhooks), + n, total, n ? total / n : 0, maxtime); } return 0; } @@ -189,7 +189,7 @@ static inline void lvm_stats_vmfree(void) } static inline void lvm_stats_memalloc(struct lvm_state *lvm, void *ptr, - size_t osize, size_t nsize) + size_t osize, size_t nsize) { } @@ -334,13 +334,14 @@ lvm_get_from_task(const struct task_struct *task, bool exclusive) struct lua_lsm_task *llt = lua_lsm_task(task); struct lvm_state *lvm = llt->lvm; int n = refcount_acquire(&lvm->refcount); + if (exclusive && n != 1) { refcount_release(&lvm->refcount); return NULL; } WARN_ON(n != 1); KASSERT(n == 1, ("<%s> Lua VM is reused, refcount = %d\n", - task->comm, n)); + task->comm, n)); return lvm->L; } @@ -349,9 +350,10 @@ static void lvm_put_to_task(const struct task_struct *task, lua_State *L) struct lua_lsm_task *llt = lua_lsm_task(task); struct lvm_state *lvm = llt->lvm; int n = refcount_release(&lvm->refcount); + KASSERT(n == 0, ("<%s> Lua VM is reused, refcount = %d\n", - task->comm, n)); - WARN_ON(L != lvm->L); + task->comm, n)); + WARN_ON(lvm->L != L); } lua_State *lvm_get(void) @@ -382,7 +384,8 @@ static int lua_shared_index(lua_State *L) int found = 0; __log_info_ratelimited("READ shared table, [%s] %s\n", - luaL_typename(L, 2), lua_tostring(L, 2) ?: "(null)"); + luaL_typename(L, 2), + lua_tostring(L, 2) ?: "(null)"); lua_pushlightuserdata(L, MODULE_KEY); lua_gettable(L, LUA_ENVIRONINDEX); /* env.MODULE_KEY */ @@ -404,9 +407,10 @@ static int lua_shared_index(lua_State *L) if (!found) { unsigned long flags; size_t l = strlen(name); + shdict = kmalloc(struct_size(shdict, name, l + 1), - lua_lsm_gfp()); - if (shdict == NULL) { + lua_lsm_gfp()); + if (!shdict) { __log_err("No memory\n"); return 0; } @@ -448,8 +452,8 @@ static int lua_shared_index(lua_State *L) static int lua_shared_newindex(lua_State *L) { __log_err("Invalid operation: WRITE shared table, [%s] %s - [%s] %s\n", - luaL_typename(L, 2), lua_tostring(L, 2) ?: "(null)", - luaL_typename(L, 3), lua_tostring(L, 3) ?: "(null)"); + luaL_typename(L, 2), lua_tostring(L, 2) ?: "(null)", + luaL_typename(L, 3), lua_tostring(L, 3) ?: "(null)"); return 0; } @@ -457,8 +461,8 @@ static int lua_module_fenv_newindex(lua_State *L) { /* args: t, k, v */ __log_warn("Warning: set global variable, [%s] %s - [%s] %s\n", - luaL_typename(L, 2), lua_tostring(L, 2) ?: "(null)", - luaL_typename(L, 3), lua_tostring(L, 3) ?: "(null)"); + luaL_typename(L, 2), lua_tostring(L, 2) ?: "(null)", + luaL_typename(L, 3), lua_tostring(L, 3) ?: "(null)"); /* XXX: t[k] = v, Warning it, but still perform assignment */ lua_rawset(L, 1); @@ -499,7 +503,7 @@ static int module_load(lua_State *L, struct lua_lsm_module *module) lua_pushcfunction(L, lua_traceback); err = luaL_loadbuffer_wrap(L, module->chunk, - module->chunk_len, module->name); + module->chunk_len, module->name); if (err) { lua_pop(L, 2); return err; @@ -518,7 +522,7 @@ static int module_load(lua_State *L, struct lua_lsm_module *module) /* stack: [env, traceback, _M] */ if (!lua_istable(L, -1)) { __log_err("module <%s> is not a table: top = %d [%s]\n", - module->name, lua_gettop(L), luaL_typename(L, -1)); + module->name, lua_gettop(L), luaL_typename(L, -1)); lua_pop(L, 3); return -EBADF; } @@ -537,14 +541,14 @@ static int lua_modules_index(lua_State *L) /* module queries are always run with a read lock */ list_for_each_entry_srcu(module, &lsm_modules, list, - srcu_read_lock_held(&modules_ss)) { + srcu_read_lock_held(&modules_ss)) { if (strcmp(module->name, key) != 0) continue; err = module_load(L, module); if (err) { __log_err("load: %s, err = %d, top = %d\n", - key, err, lua_gettop(L)); + key, err, lua_gettop(L)); return 0; } @@ -575,15 +579,14 @@ static void lua_modules_free(struct task_struct *task, lua_State *L) return; } - list_for_each_entry_srcu(module, &lsm_modules, list, - srcu_read_lock_held(&modules_ss)) { + list_for_each_entry_srcu(module, &lsm_modules, list, srcu_read_lock_held(&modules_ss)) { lua_pushstring(L, module->name); lua_rawget(L, -2); if (lua_istable(L, -1)) { atomic_dec(&module->nloaded); __log_info("<%s>: %d-%d freed module <%s>, nloaded = %d\n", - task->comm, task_tgid_nr(task), task_pid_nr(task), - module->name, atomic_read(&module->nloaded)); + task->comm, task_tgid_nr(task), task_pid_nr(task), + module->name, atomic_read(&module->nloaded)); } lua_pop(L, 1); } @@ -616,6 +619,7 @@ static void lualibs_openall(lua_State *L) static int ll_require(lua_State *L) { const char *modname = luaL_checkstring(L, 1); + luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 1); lua_getfield(L, -1, modname); /* _LOADED[modname] */ return 1; @@ -636,8 +640,9 @@ static void *lvm_alloc(void *ud, void *ptr, size_t osize, size_t nsize) static int lvm_panic(lua_State *L) { (void)L; /* to avoid warnings */ + pr_err("PANIC: unprotected error in call to Lua API (%s), top = %d\n", - lua_tostring(L, -1), lua_gettop(L)); + lua_tostring(L, -1), lua_gettop(L)); return 0; } @@ -680,7 +685,7 @@ static int lua_state_alloc(struct lvm_state *lvm) int status; L = lua_newstate(lvm_alloc, lvm); - if (L == NULL) + if (!L) return -ENOMEM; lvm->L = L; @@ -691,11 +696,11 @@ static int lua_state_alloc(struct lvm_state *lvm) status = lua_pcall(L, 0, 1, 0); if (status != 0) { __log_err("pcall: status = %d, top = %d, %s\n", - status, lua_gettop(L), lua_tostring(L, -1)); + status, lua_gettop(L), lua_tostring(L, -1)); status = -ENOEXEC; } else if (!lua_toboolean(L, -1) && lua_gettop(L) != 1) { __log_err("lvm_pmain: top = %d, stack[top] = [%s]\n", - lua_gettop(L), luaL_typename(L, -1)); + lua_gettop(L), luaL_typename(L, -1)); status = -EFAULT; } else { lua_pop(L, 1); /* pop boolean result */ @@ -771,7 +776,7 @@ int lua_lsm_module_register(const char *code, size_t len) goto err_free_lua; if (!lua_istable(L, -1)) { __log_err("pcall: top = %d [%s]\n", - lua_gettop(L), luaL_typename(L, -1)); + lua_gettop(L), luaL_typename(L, -1)); err = -EBADF; goto err_free_lua; } @@ -779,7 +784,7 @@ int lua_lsm_module_register(const char *code, size_t len) err = -ENOMEM; module = kzalloc(sizeof(*module), GFP_KERNEL); - if (module == NULL) + if (!module) goto err_free_lua; module->state = LMS_STATE_COMING; @@ -793,11 +798,16 @@ int lua_lsm_module_register(const char *code, size_t len) int type; int offset; } fields[] = { - { "name", LUA_TSTRING, offsetof(struct lua_lsm_module, name) }, - { "author", LUA_TSTRING, offsetof(struct lua_lsm_module, author) }, - { "description", LUA_TSTRING, offsetof(struct lua_lsm_module, description) }, - { "license", LUA_TSTRING, offsetof(struct lua_lsm_module, license) }, - { "version", LUA_TNUMBER, offsetof(struct lua_lsm_module, version) }, + { "name", LUA_TSTRING, + offsetof(struct lua_lsm_module, name) }, + { "author", LUA_TSTRING, + offsetof(struct lua_lsm_module, author) }, + { "description", LUA_TSTRING, + offsetof(struct lua_lsm_module, description) }, + { "license", LUA_TSTRING, + offsetof(struct lua_lsm_module, license) }, + { "version", LUA_TNUMBER, + offsetof(struct lua_lsm_module, version) }, { NULL } }; const char *key, *s; @@ -817,7 +827,7 @@ int lua_lsm_module_register(const char *code, size_t len) if (lua_type(L, -1) != fields[i].type) { __log_err("field '%s' must be a %s\n", - key, lua_typename(L, fields[i].type)); + key, lua_typename(L, fields[i].type)); break; } @@ -834,7 +844,7 @@ int lua_lsm_module_register(const char *code, size_t len) break; } - if (fields[i].field == NULL) { + if (!fields[i].field) { for (i = 0; lua_lsm_hook_stats[i].name; i++) { if (strcmp(lua_lsm_hook_stats[i].name, key) != 0) continue; @@ -851,7 +861,7 @@ int lua_lsm_module_register(const char *code, size_t len) break; } - if (lua_lsm_hook_stats[i].name == NULL) + if (!lua_lsm_hook_stats[i].name) __log_warn("field '%s' is unknown\n", key); } @@ -860,7 +870,7 @@ int lua_lsm_module_register(const char *code, size_t len) } err = -ENOMEM; - if (module->name == NULL) + if (!module->name) goto err_free_module; /* Recompile with the new name */ @@ -881,10 +891,10 @@ int lua_lsm_module_register(const char *code, size_t len) /* stack: [traceback, _M, func, chunk] */ chunk = lua_tolstring(L, -1, &chunk_len); __log_info("[%s] compiled, source_len = %d, chunk_len = %d\n", - module->name, (int)len, (int)chunk_len); + module->name, (int)len, (int)chunk_len); module->chunk = kmalloc(chunk_len, GFP_KERNEL); - if (module->chunk == NULL) + if (!module->chunk) goto err_free_module; memcpy(module->chunk, chunk, chunk_len); module->chunk_len = chunk_len; @@ -971,7 +981,7 @@ static int task_remove_module(struct task_struct *task, void *arg) return -EBUSY; L = lvm_get_from_task(task, true); - if (L == NULL) + if (!L) return -EAGAIN; err = lvm_remove_module(L, module); @@ -997,16 +1007,17 @@ static int tasks_lvm_remove_module(struct lua_lsm_module *module, int *nbusy) if (!err) { count++; __log_info("<%s>: err = [ OK ] \t<%s>: %d-%d\n", module->name, - task->comm, task_tgid_nr(task), task_pid_nr(task)); + task->comm, task_tgid_nr(task), task_pid_nr(task)); } else if (err == -EBUSY || err == -EAGAIN) { *nbusy += 1; __log_info("<%s>: err = %s \t<%s>: %d-%d\n", - module->name, err == -EBUSY ? "EBUSY" : "EAGAIN", - task->comm, task_tgid_nr(task), task_pid_nr(task)); + module->name, err == -EBUSY ? "EBUSY" : "EAGAIN", + task->comm, task_tgid_nr(task), task_pid_nr(task)); } else { __log_info_ratelimited("<%s>: err = %s \t<%s>: %d-%d\n", - module->name, err == -ENOENT ? "[ENOENT]" : "unknown", - task->comm, task_tgid_nr(task), task_pid_nr(task)); + module->name, + err == -ENOENT ? "[ENOENT]" : "unknown", + task->comm, task_tgid_nr(task), task_pid_nr(task)); } } read_unlock(&tasklist_lock); @@ -1026,7 +1037,7 @@ static void softirq_lvm_remove_module(struct work_struct *work) int cpu = smp_processor_id(); int err; - WARN_ON(work_ctx_remove_module == NULL); + WARN_ON(!work_ctx_remove_module); /* * Disable softirq to prevent triggered softirq or RCU from * changing the Lua VM environment. @@ -1036,7 +1047,7 @@ static void softirq_lvm_remove_module(struct work_struct *work) if (!err) { atomic_inc(&work_ctx_remove_count); __log_info("<%s>: err = [ OK ] \t, count = %d\n", - module->name, cpu, atomic_read(&work_ctx_remove_count)); + module->name, cpu, atomic_read(&work_ctx_remove_count)); } local_bh_enable(); } @@ -1091,7 +1102,7 @@ int lua_lsm_module_unregister(const char *name) count += tasks_lvm_remove_module(module, &nbusy); __log_info("Unregister module <%s> from task, freed = %d/%d, nbusy = %d\n", - name, count, nloaded, nbusy); + name, count, nloaded, nbusy); } /* @@ -1112,7 +1123,7 @@ int lua_lsm_module_unregister(const char *name) count += atomic_read(&work_ctx_remove_count); __log_info("Unregister module <%s> from pcpu, freed = %d/%d\n", - name, count, nloaded); + name, count, nloaded); } nloaded = atomic_read(&module->nloaded); @@ -1128,7 +1139,7 @@ int lua_lsm_module_unregister(const char *name) cpus_read_unlock(); __log_info("Unregister module <%s> from swapper, freed = %d/%d\n", - name, count, atomic_read(&module->nloaded)); + name, count, atomic_read(&module->nloaded)); } nloaded = atomic_read(&module->nloaded); @@ -1175,18 +1186,17 @@ int modules_show(struct seq_file *m, void *v) seq_printf(m, "modules for lua-lsm\n"); seq_printf(m, "%-20s %-10s %6s %4s %5s %6s %6s %-34s\n", - "name", "license", "size", "nlsm", - "nload", "shdict", "kvnode", "author"); + "name", "license", "size", "nlsm", + "nload", "shdict", "kvnode", "author"); seq_printf(m, "%s\n", TABLINE); idx = srcu_read_lock(&modules_ss); - list_for_each_entry_srcu(module, &lsm_modules, list, - srcu_read_lock_held(&modules_ss)) { + list_for_each_entry_srcu(module, &lsm_modules, list, srcu_read_lock_held(&modules_ss)) { seq_printf(m, "%-20s %-10s %6zu %4d %5d %6d %6d %-34s\n", - module->name, module->license, module->chunk_len, - module->nhooks, atomic_read(&module->nloaded), - atomic_read(&module->shdict_count), - atomic_read(&module->kvnodes_count), module->author); + module->name, module->license, module->chunk_len, + module->nhooks, atomic_read(&module->nloaded), + atomic_read(&module->shdict_count), + atomic_read(&module->kvnodes_count), module->author); } srcu_read_unlock(&modules_ss, idx); return 0; @@ -1265,7 +1275,6 @@ struct lsm_blob_sizes lua_lsm_blob_sizes __ro_after_init = { .lbs_bdev = sizeof(struct lua_lsm_object), }; - static struct security_hook_list lua_lsm_hooks[] __ro_after_init = { #define LSM_HOOK(RET, DEFAULT, NAME, ...) \ LSM_HOOK_INIT(NAME, lua_lsm_ ## NAME), @@ -1296,7 +1305,7 @@ static int __init lua_lsm_init(void) for_each_possible_cpu(cpu) { lvm = kzalloc(sizeof(struct lvm_state), GFP_KERNEL); - if (lvm == NULL) + if (!lvm) return -ENOMEM; err = lua_state_alloc(lvm); diff --git a/security/lua/lsm.h b/security/lua/lsm.h index 0f3bd3bd2941..52a66cdd3484 100644 --- a/security/lua/lsm.h +++ b/security/lua/lsm.h @@ -25,7 +25,6 @@ extern int lua_lsm_initialized __initdata; #define LUA_LSM_VERSION 1 - struct lua_lsm_hook_stat { const char *name; atomic_t nhooks; @@ -38,7 +37,6 @@ struct lua_lsm_hook_stat { extern struct lua_lsm_hook_stat lua_lsm_hook_stats[]; - #define LSM_HOOK(RET, DEFAULT, NAME, ...) \ int __prepare_ ## NAME(__VA_ARGS__); \ void __postpone_ ## NAME(__VA_ARGS__); @@ -46,7 +44,6 @@ extern struct lua_lsm_hook_stat lua_lsm_hook_stats[]; #include #undef LSM_HOOK - enum { #define LSM_HOOK(RET, DEFAULT, NAME, ...) __LL_NR_ ## NAME, #include @@ -75,15 +72,20 @@ struct lua_lsm_module { int version; enum lua_lsm_module_state state; struct list_head list; + __BITMAP_TYPE(, uint32_t, __LL_NR_MAX) hookfuncs; int nhooks; char *chunk; size_t chunk_len; atomic_t nloaded; struct list_head shdicts; + + /* Protects shdicts and shdict_count. */ spinlock_t shdict_lock; atomic_t shdict_count; struct list_head kvnodes; + + /* Protects kvnodes and kvnodes_count. */ spinlock_t kvnodes_lock; atomic_t kvnodes_count; }; @@ -91,7 +93,6 @@ struct lua_lsm_module { extern struct list_head lsm_modules; extern struct srcu_struct modules_ss; - #define TABLINE \ "---------------------------------------------" \ "---------------------------------------------" diff --git a/security/lua/lua_capability.c b/security/lua/lua_capability.c index 22064c92d79f..b31ae75d52d1 100644 --- a/security/lua/lua_capability.c +++ b/security/lua/lua_capability.c @@ -62,32 +62,38 @@ static int capability_add(lua_State *L) { kernel_cap_t cap1 = tocap(L, 1); int tt = lua_type(L, 2); + int cap; + if (tt == LUA_TUSERDATA) { const kernel_cap_t cap2 = tocap(L, 2); + *newcap(L) = cap_combine(cap1, cap2); return 1; - } else { - int cap = arg2cap(L, 2); - cap_raise(cap1, cap); - *newcap(L) = cap1; - return 1; } + + cap = arg2cap(L, 2); + cap_raise(cap1, cap); + *newcap(L) = cap1; + return 1; } static int capability_sub(lua_State *L) { kernel_cap_t cap1 = tocap(L, 1); int tt = lua_type(L, 2); + int cap; + if (tt == LUA_TUSERDATA) { const kernel_cap_t cap2 = tocap(L, 2); + *newcap(L) = cap_drop(cap1, cap2); return 1; - } else { - int cap = arg2cap(L, 2); - cap_lower(cap1, cap); - *newcap(L) = cap1; - return 1; } + + cap = arg2cap(L, 2); + cap_lower(cap1, cap); + *newcap(L) = cap1; + return 1; } static int capability_mul(lua_State *L) @@ -102,6 +108,7 @@ static int capability_eq(lua_State *L) { const kernel_cap_t cap1 = tocap(L, 1); const kernel_cap_t cap2 = tocap(L, 2); + lua_pushboolean(L, cap_isidentical(cap1, cap2)); return 1; } @@ -110,6 +117,7 @@ static int capability_le(lua_State *L) { const kernel_cap_t cap1 = tocap(L, 1); const kernel_cap_t cap2 = tocap(L, 2); + lua_pushboolean(L, cap_issubset(cap1, cap2)); return 1; } @@ -119,6 +127,7 @@ static int capability_lt(lua_State *L) const kernel_cap_t cap1 = tocap(L, 1); const kernel_cap_t cap2 = tocap(L, 2); bool b = cap_issubset(cap1, cap2) && !cap_isidentical(cap1, cap2); + lua_pushboolean(L, b); return 1; } diff --git a/security/lua/lua_errno.c b/security/lua/lua_errno.c index 8ca4b625ec51..83c1ae347465 100644 --- a/security/lua/lua_errno.c +++ b/security/lua/lua_errno.c @@ -52,6 +52,7 @@ static const struct const_value errnos[] = { static int errno_errname(lua_State *L) { int err = luaL_checkinteger(L, 1); + lua_pushstring(L, errname(err)); return 1; } diff --git a/security/lua/lua_fs.c b/security/lua/lua_fs.c index 0460867a1ad0..3f1abdb861f5 100644 --- a/security/lua/lua_fs.c +++ b/security/lua/lua_fs.c @@ -18,11 +18,10 @@ #include "kvcache.h" #include "lua_object.h" - /********************************** util **********************************/ static int fs_xattr(lua_State *L, struct dentry *dentry, - struct inode *inode, const char *name) + struct inode *inode, const char *name) { char buffer[128]; ssize_t len; @@ -36,14 +35,15 @@ static int fs_xattr(lua_State *L, struct dentry *dentry, if (len == 0) { lua_pushnil(L); return 1; - } else if (len < 0) { + } + if (len < 0) { lua_pushnil(L); lua_pushinteger(L, -len); return 2; - } else { - lua_pushlstring(L, (const char *)buffer, len); - return 1; } + + lua_pushlstring(L, (const char *)buffer, len); + return 1; } /********************************** dentry **********************************/ @@ -51,6 +51,7 @@ static int fs_xattr(lua_State *L, struct dentry *dentry, static int fs_dentry_dget(lua_State *L) { struct dentry *dentry = todentry(L, 1); + dget(dentry); lua_settop(L, 1); return 1; @@ -59,6 +60,7 @@ static int fs_dentry_dget(lua_State *L) static int fs_dentry_dput(lua_State *L) { struct dentry *dentry = todentry(L, 1); + dput(dentry); return 0; } @@ -66,6 +68,7 @@ static int fs_dentry_dput(lua_State *L) static int fs_dentry_backing_inode(lua_State *L) { struct dentry *dentry = todentry(L, 1); + *newinode(L) = d_backing_inode(dentry); return 1; } @@ -75,6 +78,7 @@ static int fs_dentry_xattr(lua_State *L) struct dentry *dentry = todentry(L, 1); struct inode *inode = toinode(L, 2); const char *name = luaL_checkstring(L, 3); + return fs_xattr(L, dentry, inode, name); } @@ -82,6 +86,7 @@ static int fs_dentry_path(lua_State *L) { struct dentry *dentry = todentry(L, 1); int rawpath = lua_toboolean(L, 2); + return aux_dentry_path(L, dentry, rawpath); } @@ -89,6 +94,7 @@ static int meth_dentry_tostring(lua_State *L) { struct dentry *dentry = todentry(L, 1); int nres = aux_dentry_path(L, dentry, 0); + if (nres == 2) lua_pushfstring(L, "dentry: ", lua_tostring(L, -1)); else @@ -111,6 +117,7 @@ static const luaL_Reg fs_dentry_meth[] = { static int fs_inode_ino(lua_State *L) { struct inode *inode = toinode(L, 1); + lua_pushinteger(L, inode->i_ino); return 1; } @@ -120,12 +127,14 @@ static int fs_inode_xattr(lua_State *L) struct inode *inode = toinode(L, 1); struct dentry *dentry = todentry(L, 2); const char *name = luaL_checkstring(L, 3); + return fs_xattr(L, dentry, inode, name); } static int fs_inode_size(lua_State *L) { struct inode *inode = toinode(L, 1); + lua_pushinteger(L, i_size_read(inode)); return 1; } @@ -149,6 +158,7 @@ static int fs_inode_filetype(lua_State *L) struct inode *inode = toinode(L, 1); int top = lua_gettop(L); int i; + if (top == 1) { for (i = 0; opts[i].name; i++) { if ((inode->i_mode & S_IFMT) != opts[i].flag) @@ -158,11 +168,12 @@ static int fs_inode_filetype(lua_State *L) } } else if (top == 2) { const char *s = luaL_checkstring(L, 2); + for (i = 0; opts[i].name; i++) { if (strcasecmp(s, opts[i].name) != 0) continue; lua_pushboolean(L, - (inode->i_mode & S_IFMT) == opts[i].flag); + (inode->i_mode & S_IFMT) == opts[i].flag); return 1; } } @@ -193,11 +204,13 @@ static int fs_inode_mode(lua_State *L) }; struct inode *inode = toinode(L, 1); int top = lua_gettop(L); + if (top >= 2) { int start = (top == 2) ? 2 : 3; int and = lua_isboolean(L, 2) && lua_toboolean(L, 2); unsigned int flags = tocflags(L, start, top, opts, 0); unsigned int res = inode->i_mode & flags; + lua_pushboolean(L, and ? res == flags : (int)res); return 1; } else if (top == 1) { @@ -210,6 +223,7 @@ static int fs_inode_mode(lua_State *L) static int fs_inode_ids(lua_State *L) { struct inode *inode = toinode(L, 1); + lua_pushinteger(L, inode->i_uid.val); lua_pushinteger(L, inode->i_gid.val); return 2; @@ -220,6 +234,7 @@ static int meth_inode_tostring(lua_State *L) struct inode *inode = toinode(L, 1); char buffer[128]; int l = snprintf(buffer, sizeof(buffer), "inode: [%lu]", inode->i_ino); + lua_pushlstring(L, buffer, l); return 1; } @@ -255,12 +270,14 @@ static int fs_file_xattr(lua_State *L) { struct file *file = tofile(L, 1); const char *name = luaL_checkstring(L, 2); + return fs_xattr(L, file_dentry(file), file_inode(file), name); } static int fs_file_size(lua_State *L) { struct file *file = tofile(L, 1); + lua_pushinteger(L, i_size_read(file_inode(file))); return 1; } @@ -282,11 +299,13 @@ static int fs_file_fmode(lua_State *L) }; struct file *file = tofile(L, 1); int top = lua_gettop(L); + if (top >= 2) { int start = (top == 2) ? 2 : 3; int and = lua_isboolean(L, 2) && lua_toboolean(L, 2); fmode_t flags = tocflags(L, start, top, opts, 0); fmode_t res = file->f_mode & flags; + lua_pushboolean(L, and ? res == flags : (int)res); return 1; } else if (top == 1) { @@ -299,6 +318,7 @@ static int fs_file_fmode(lua_State *L) static int fs_file_path(lua_State *L) { struct file *file = tofile(L, 1); + return aux_file_path(L, file); } @@ -306,6 +326,7 @@ static int meth_file_tostring(lua_State *L) { struct file *file = tofile(L, 1); int nres = aux_file_path(L, file); + if (nres == 2) lua_pushfstring(L, "file: ", lua_tostring(L, -1)); else @@ -316,6 +337,7 @@ static int meth_file_tostring(lua_State *L) static int meth_file_gc(lua_State *L) { struct file **filp = togcfilep(L, 1); + if (*filp) { fput(*filp); *filp = NULL; @@ -388,6 +410,7 @@ static int fs_path_eq(lua_State *L) { struct path *path1 = topath(L, 1); struct path *path2 = topath(L, 2); + lua_pushboolean(L, path_equal(path1, path2)); return 1; } @@ -396,6 +419,7 @@ static int meth_path_tostring(lua_State *L) { struct path *path = topath(L, 1); int nres = aux_dentry_path(L, path->dentry, 0); + if (nres == 2) lua_pushfstring(L, "path: ", lua_tostring(L, -1)); else @@ -416,6 +440,7 @@ static const luaL_Reg fs_path_meth[] = { static int fs_superblock_magic(lua_State *L) { struct super_block *sb = tosuperblock(L, 1); + lua_pushinteger(L, sb->s_magic); return 1; } @@ -431,6 +456,7 @@ static int fs_superblock_fstype_name(lua_State *L) { struct super_block *sb = tosuperblock(L, 1); struct file_system_type *type = sb->s_type; + if (type) lua_pushstring(L, type->name); else @@ -442,6 +468,7 @@ static int meth_superblock_tostring(lua_State *L) { struct super_block *sb = tosuperblock(L, 1); struct file_system_type *type = sb->s_type; + lua_pushfstring(L, "superblock: '%s'", type ? type->name : ""); return 1; } @@ -499,6 +526,7 @@ static int fs_mntidmap_inode_owner_or_capable(lua_State *L) { struct mnt_idmap *idmap = tomntidmap(L, 1); const struct inode *inode = toinode(L, 2); + lua_pushboolean(L, inode_owner_or_capable(idmap, inode)); return 1; } @@ -508,6 +536,7 @@ static int fs_mntidmap_capable_wrt_inode_uidgid(lua_State *L) struct mnt_idmap *idmap = tomntidmap(L, 1); const struct inode *inode = toinode(L, 2); int cap = luaL_checkint(L, 3); + lua_pushboolean(L, capable_wrt_inode_uidgid(idmap, inode, cap)); return 1; } diff --git a/security/lua/lua_kernel.c b/security/lua/lua_kernel.c index 71e844bdea85..2507c3e659ac 100644 --- a/security/lua/lua_kernel.c +++ b/security/lua/lua_kernel.c @@ -20,12 +20,12 @@ #include "kvcache.h" #include "lua_object.h" - /********************************** cred **********************************/ static int kernel_cred_uids(lua_State *L) { const struct cred *cred = tocred(L, 1); + lua_pushinteger(L, cred->uid.val); lua_pushinteger(L, cred->euid.val); lua_pushinteger(L, cred->suid.val); @@ -36,6 +36,7 @@ static int kernel_cred_uids(lua_State *L) static int kernel_cred_gids(lua_State *L) { const struct cred *cred = tocred(L, 1); + lua_pushinteger(L, cred->gid.val); lua_pushinteger(L, cred->egid.val); lua_pushinteger(L, cred->sgid.val); @@ -47,6 +48,7 @@ static int kernel_cred_cap_eip(lua_State *L) { struct cred *cred = tocred(L, 1); int top = lua_gettop(L); + if (top > 4) return luaL_error(L, "wrong number of arguments"); if (top == 1) { @@ -70,27 +72,29 @@ static int kernel_cred_cap_eip(lua_State *L) static int kernel_cred_cap_bset(lua_State *L) { struct cred *cred = tocred(L, 1); + if (lua_gettop(L) == 1) { *newcap(L) = cred->cap_bset; return 1; - } else { - cred->cap_bset = tocap(L, 2); - lua_settop(L, 1); - return 1; } + + cred->cap_bset = tocap(L, 2); + lua_settop(L, 1); + return 1; } static int kernel_cred_cap_ambient(lua_State *L) { struct cred *cred = tocred(L, 1); + if (lua_gettop(L) == 1) { *newcap(L) = cred->cap_ambient; return 1; - } else { - cred->cap_ambient = tocap(L, 2); - lua_settop(L, 1); - return 1; } + + cred->cap_ambient = tocap(L, 2); + lua_settop(L, 1); + return 1; } /* @@ -111,14 +115,17 @@ static int kernel_cred_securebits(lua_State *L) }; struct cred *cred = tocred(L, 1); int top = lua_gettop(L); + if (top >= 2) { int start = (top == 2) ? 2 : 3; int and = lua_isboolean(L, 2) && lua_toboolean(L, 2); unsigned int flags = tocflags(L, start, top, opts, 0); unsigned int res = cred->securebits & flags; + lua_pushboolean(L, and ? res == flags : (int)res); return 1; - } else if (top == 1) { + } + if (top == 1) { table_fromopts(L, opts, 0, (unsigned int)cred->securebits); return 1; } @@ -137,10 +144,10 @@ static const luaL_Reg cred_meth[] = { /********************************** task **********************************/ - static int kernel_task_pids(lua_State *L) { struct task_struct *task = totask(L, 1); + lua_pushinteger(L, task->pid); lua_pushinteger(L, task->tgid); return 2; @@ -156,6 +163,7 @@ static int kernel_task_cred(lua_State *L) static int kernel_task_comm(lua_State *L) { struct task_struct *task = totask(L, 1); + lua_pushstring(L, task->comm); return 1; } @@ -163,6 +171,7 @@ static int kernel_task_comm(lua_State *L) static int kernel_task_nr_threads(lua_State *L) { struct task_struct *task = totask(L, 1); + lua_pushinteger(L, get_nr_threads(task)); return 1; } @@ -170,17 +179,19 @@ static int kernel_task_nr_threads(lua_State *L) static int kernel_task_group_leader(lua_State *L) { struct task_struct *task = totask(L, 1); - if (!thread_group_leader(task)) { + + if (!thread_group_leader(task)) *newtask(L) = rcu_dereference(task->group_leader); - } else { + else lua_settop(L, 1); - } + return 1; } static int kernel_task_thread_group_leader(lua_State *L) { struct task_struct *task = totask(L, 1); + lua_pushboolean(L, thread_group_leader(task)); return 1; } @@ -189,6 +200,7 @@ static int kernel_task_same_thread_group(lua_State *L) { struct task_struct *task1 = totask(L, 1); struct task_struct *task2 = totask(L, 2); + lua_pushboolean(L, same_thread_group(task1, task2)); return 1; } @@ -199,6 +211,7 @@ static int kernel_task_same_group_ptracer(lua_State *L) struct task_struct *tracer = totask(L, 2); struct task_struct *parent; int res = 0; + rcu_read_lock(); parent = ptrace_parent(tracee); if (parent && same_thread_group(parent, tracer)) @@ -211,6 +224,7 @@ static int kernel_task_same_group_ptracer(lua_State *L) static int kernel_task_is_idle(lua_State *L) { struct task_struct *task = totask(L, 1); + lua_pushboolean(L, is_idle_task(task)); return 1; } @@ -219,13 +233,14 @@ static int kernel_task_exe_file(lua_State *L) { struct task_struct *task = totask(L, 1); struct file *exe_file; + if (spin_is_locked(&task->alloc_lock)) { lua_pushnil(L); lua_pushstring(L, "busy"); return 2; } exe_file = get_task_exe_file(task); - if (exe_file == NULL) + if (!exe_file) return 0; *newgcfile(L) = exe_file; return 1; @@ -236,8 +251,10 @@ static int kernel_task_exepath(lua_State *L) struct task_struct *task = totask(L, 1); struct file *file; int nres = 0; + if (task == current) { struct mm_struct *mm = current->mm; + if (!mm) return 0; file = get_mm_exe_file(mm); @@ -260,6 +277,7 @@ static int kernel_task_cmdline(lua_State *L) { struct task_struct *task = totask(L, 1); char *cmdline = kstrdup_quotable_cmdline(task, lua_lsm_gfp()); + lua_pushstring(L, cmdline); kfree(cmdline); return 1; @@ -269,6 +287,7 @@ static int kernel_task_capable(lua_State *L) { struct task_struct *task = totask(L, 1); int nres; + rcu_read_lock(); nres = aux_capable(L, __task_cred(task), 2); rcu_read_unlock(); @@ -285,7 +304,7 @@ static int kernel_task_is_descendant(lua_State *L) switch (tt) { case LUA_TNUMBER: parent = find_get_task_by_vpid((pid_t)lua_tointeger(L, 2)); - if (parent == NULL) + if (!parent) return luaL_argerror(L, 2, "invalid pid"); break; case LUA_TUSERDATA: @@ -319,6 +338,7 @@ static int kernel_task_is_descendant(lua_State *L) static int kernel_task_pid_alive(lua_State *L) { struct task_struct *task = totask(L, 1); + lua_pushboolean(L, pid_alive(task)); return 1; } @@ -326,8 +346,9 @@ static int kernel_task_pid_alive(lua_State *L) static int meth_task_tostring(lua_State *L) { struct task_struct *task = totask(L, 1); + lua_pushfstring(L, "task: '%s'", task->comm); - return 1; + return 1; } static const luaL_Reg task_meth[] = { @@ -353,6 +374,7 @@ static const luaL_Reg task_meth[] = { static int meth_task_gc(lua_State *L) { struct task_struct **taskp = togctaskp(L, 1); + if (*taskp) { put_task_struct(*taskp); *taskp = NULL; @@ -389,6 +411,7 @@ static int kernel_lsm_funcs(lua_State *L) #undef LSM_HOOK }; int i; + lua_createtable(L, ARRAY_SIZE(lsm_funcs), 0); for (i = 0; i < ARRAY_SIZE(lsm_funcs); i++) { lua_createtable(L, 3, 0); @@ -408,6 +431,7 @@ static int kernel_random(lua_State *L) { int l, u; u32 r; + switch (lua_gettop(L)) { case 0: r = get_random_u32(); @@ -436,6 +460,7 @@ static int kernel_ktime_seconds(lua_State *L) { int monotonic = lua_toboolean(L, 1); time64_t sec; + if (monotonic) sec = ktime_get_seconds(); else @@ -460,7 +485,8 @@ static int kernel_task_from_pid(lua_State *L) { pid_t nr = (pid_t)luaL_checkinteger(L, 1); struct task_struct *task = find_get_task_by_vpid(nr); - if (task == NULL) + + if (!task) return 0; *newgctask(L) = task; return 1; @@ -469,6 +495,7 @@ static int kernel_task_from_pid(lua_State *L) static int kernel_printk(lua_State *L) { const char *s = luaL_checkstring(L, 1); + pr_info("%s\n", s); return 0; } diff --git a/security/lua/lua_net.c b/security/lua/lua_net.c index 94898f51b4c8..39ba4d925f7b 100644 --- a/security/lua/lua_net.c +++ b/security/lua/lua_net.c @@ -24,21 +24,49 @@ static const char *family_tostring(sa_family_t sa_family) { const char *family = NULL; + switch (sa_family) { - case AF_UNSPEC: family = "unspec"; break; - case AF_INET: family = "inet"; break; - case AF_INET6: family = "inet6"; break; - case AF_UNIX: family = "unix"; break; - case AF_NETLINK: family = "netlink"; break; - case AF_PACKET: family = "packet"; break; - case AF_KEY: family = "key"; break; - case AF_APPLETALK: family = "appletalk"; break; - case AF_ALG: family = "alg"; break; - case AF_NFC: family = "nfc"; break; - case AF_VSOCK: family = "vsock"; break; - case AF_KCM: family = "kcm"; break; - case AF_SMC: family = "smc"; break; + case AF_UNSPEC: + family = "unspec"; + break; + case AF_INET: + family = "inet"; + break; + case AF_INET6: + family = "inet6"; + break; + case AF_UNIX: + family = "unix"; + break; + case AF_NETLINK: + family = "netlink"; + break; + case AF_PACKET: + family = "packet"; + break; + case AF_KEY: + family = "key"; + break; + case AF_APPLETALK: + family = "appletalk"; + break; + case AF_ALG: + family = "alg"; + break; + case AF_NFC: + family = "nfc"; + break; + case AF_VSOCK: + family = "vsock"; + break; + case AF_KCM: + family = "kcm"; + break; + case AF_SMC: + family = "smc"; + break; } + return family; } @@ -56,6 +84,7 @@ static int net_sock_suites(lua_State *L) struct sock *sk = tosock(L, 1); const char *family = "unknown", *type = "unknown", *protocol = "unknown"; int nres = 0; + if (lua_gettop(L) >= 2) { if (!lua_toboolean(L, 2)) family = NULL; @@ -66,41 +95,85 @@ static int net_sock_suites(lua_State *L) } if (family) { family = family_tostring(sk->sk_family); - if (family == NULL) + if (!family) family = "unknown"; lua_pushstring(L, family); nres += 1; } if (type) { switch (sk->sk_type) { - case SOCK_STREAM: type = "stream"; break; - case SOCK_DGRAM: type = "dgram"; break; - case SOCK_RAW: type = "raw"; break; - case SOCK_RDM: type = "rdm"; break; - case SOCK_SEQPACKET: type = "seqpacket"; break; - case SOCK_DCCP: type = "dccp"; break; - case SOCK_PACKET: type = "packet"; break; + case SOCK_STREAM: + type = "stream"; + break; + case SOCK_DGRAM: + type = "dgram"; + break; + case SOCK_RAW: + type = "raw"; + break; + case SOCK_RDM: + type = "rdm"; + break; + case SOCK_SEQPACKET: + type = "seqpacket"; + break; + case SOCK_DCCP: + type = "dccp"; + break; + case SOCK_PACKET: + type = "packet"; + break; } lua_pushstring(L, type); nres += 1; } if (protocol) { switch (sk->sk_protocol) { - case IPPROTO_IP: protocol = "ip"; break; - case IPPROTO_ICMP: protocol = "icmp"; break; - case IPPROTO_IGMP: protocol = "igmp"; break; - case IPPROTO_TCP: protocol = "tcp"; break; - case IPPROTO_EGP: protocol = "egp"; break; - case IPPROTO_UDP: protocol = "udp"; break; - case IPPROTO_DCCP: protocol = "dccp"; break; - case IPPROTO_IPV6: protocol = "ipv6"; break; - case IPPROTO_ESP: protocol = "esp"; break; - case IPPROTO_L2TP: protocol = "l2tp"; break; - case IPPROTO_SCTP: protocol = "sctp"; break; - case IPPROTO_UDPLITE: protocol = "udplite"; break; - case IPPROTO_RAW: protocol = "raw"; break; - case IPPROTO_SMC: protocol = "smc"; break; - case IPPROTO_MPTCP: protocol = "mptcp"; break; + case IPPROTO_IP: + protocol = "ip"; + break; + case IPPROTO_ICMP: + protocol = "icmp"; + break; + case IPPROTO_IGMP: + protocol = "igmp"; + break; + case IPPROTO_TCP: + protocol = "tcp"; + break; + case IPPROTO_EGP: + protocol = "egp"; + break; + case IPPROTO_UDP: + protocol = "udp"; + break; + case IPPROTO_DCCP: + protocol = "dccp"; + break; + case IPPROTO_IPV6: + protocol = "ipv6"; + break; + case IPPROTO_ESP: + protocol = "esp"; + break; + case IPPROTO_L2TP: + protocol = "l2tp"; + break; + case IPPROTO_SCTP: + protocol = "sctp"; + break; + case IPPROTO_UDPLITE: + protocol = "udplite"; + break; + case IPPROTO_RAW: + protocol = "raw"; + break; + case IPPROTO_SMC: + protocol = "smc"; + break; + case IPPROTO_MPTCP: + protocol = "mptcp"; + break; } lua_pushstring(L, protocol); nres += 1; @@ -112,6 +185,7 @@ static int net_sock_listener(lua_State *L) { struct sock *sk = tosock(L, 1); int time_wait = lua_toboolean(L, 2); + lua_pushboolean(L, time_wait ? sk_listener_or_tw(sk) : sk_listener(sk)); return 1; } @@ -147,6 +221,7 @@ static const luaL_Reg sock_meth[] = { static int net_socket_release(lua_State *L) { struct socket **sockp = tosocketp(L, 1); + if (*sockp) { sock_release(*sockp); *sockp = NULL; @@ -195,12 +270,23 @@ static int net_skb_protocol(lua_State *L) { struct sk_buff *skb = toskb(L, 1); const char *protocol = "unknown"; + switch (skb->protocol) { - case htons(ETH_P_LOOP): protocol = "loop"; break; - case htons(ETH_P_IP): protocol = "ip"; break; - case htons(ETH_P_IPV6): protocol = "ipv6"; break; - case htons(ETH_P_ARP): protocol = "arp"; break; - case htons(ETH_P_RARP): protocol = "rarp"; break; + case htons(ETH_P_LOOP): + protocol = "loop"; + break; + case htons(ETH_P_IP): + protocol = "ip"; + break; + case htons(ETH_P_IPV6): + protocol = "ipv6"; + break; + case htons(ETH_P_ARP): + protocol = "arp"; + break; + case htons(ETH_P_RARP): + protocol = "rarp"; + break; } lua_pushstring(L, protocol); return 1; @@ -209,6 +295,7 @@ static int net_skb_protocol(lua_State *L) static int net_skb_iif(lua_State *L) { struct sk_buff *skb = toskb(L, 1); + lua_pushinteger(L, skb->skb_iif); return 1; } @@ -216,6 +303,7 @@ static int net_skb_iif(lua_State *L) static int net_skb_secmark(lua_State *L) { struct sk_buff *skb = toskb(L, 1); + lua_pushinteger(L, skb->secmark); return 1; } @@ -235,7 +323,8 @@ static int sockaddr_family(lua_State *L) { struct sockaddr *sa = tosockaddr(L, 1); const char *family = family_tostring(sa->sa_family); - if (family == NULL) + + if (!family) family = "unknown"; lua_pushstring(L, family); return 1; @@ -247,6 +336,7 @@ static int sockaddr_addrs(lua_State *L) int readable = lua_toboolean(L, 2); char buffer[128]; int l; + switch (sa->sa_family) { case AF_INET: lua_pushstring(L, "inet"); @@ -264,8 +354,9 @@ static int sockaddr_addrs(lua_State *L) l = snprintf(buffer, sizeof(buffer), "%pISc", sa); lua_pushlstring(L, buffer, l); } else { - lua_pushlstring(L, ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr, - sizeof(((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr)); + lua_pushlstring(L, + ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr, + sizeof(((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr)); } lua_pushinteger(L, ntohs(((struct sockaddr_in6 *)sa)->sin6_port)); return 3; @@ -282,6 +373,7 @@ static int meth_sockaddr_tostring(lua_State *L) struct sockaddr *sa = tosockaddr(L, 1); char buffer[128]; int l; + switch (sa->sa_family) { case AF_INET: l = snprintf(buffer, sizeof(buffer), "sa.inet: %pISpc", sa); @@ -316,8 +408,9 @@ static const luaL_Reg sockaddr_meth[] = { static int net_sock_alloc(lua_State *L) { struct socket **sockp = newsocket(L); + *sockp = sock_alloc(); - if (*sockp == NULL) + if (!*sockp) return 0; return 1; } @@ -338,6 +431,7 @@ INET_HN_DEFINE(ntohs) static int net_in_aton(lua_State *L) { const char *s = luaL_checkstring(L, 1); + lua_pushinteger(L, in_aton(s)); return 1; } @@ -347,6 +441,7 @@ static int net_in4_pton(lua_State *L) size_t len; const char *s = luaL_checklstring(L, 1, &len); __be32 addr; + if (in4_pton(s, (int)len, (u8 *)&addr, '\n', NULL)) lua_pushinteger(L, addr); else @@ -359,6 +454,7 @@ static int net_in6_pton(lua_State *L) size_t len; const char *s = luaL_checklstring(L, 1, &len); __u8 addr[16]; + if (in6_pton(s, (int)len, (u8 *)addr, '\n', NULL)) lua_pushlstring(L, addr, sizeof(addr)); else @@ -378,7 +474,6 @@ static const luaL_Reg netlib[] = { { NULL, NULL } }; - LUALIB_API int luaopen_net(lua_State *L) { luaL_newlib(L, netlib); diff --git a/security/lua/refcount.h b/security/lua/refcount.h index b59fd8e21cba..e957f1e759d7 100644 --- a/security/lua/refcount.h +++ b/security/lua/refcount.h @@ -18,7 +18,6 @@ } \ } while (0) - static inline void refcount_init(atomic_t *count, long value) { @@ -36,6 +35,7 @@ static inline int refcount_release(atomic_t *count) { int n = atomic_dec_return(count); + KASSERT(n >= 0, ("negative refcount %p", count)); return n; } diff --git a/security/lua/securityfs.c b/security/lua/securityfs.c index 160c40b6d9ea..16942c659872 100644 --- a/security/lua/securityfs.c +++ b/security/lua/securityfs.c @@ -10,7 +10,6 @@ #include #include "lsm.h" - static bool lua_lsm_capable(int cap) { bool allow = true; @@ -50,7 +49,7 @@ static const struct file_operations fops_version = { }; static ssize_t module_write(const char __user *buf, size_t len, - loff_t *ppos, int load) + loff_t *ppos, int load) { char *buffer, *p; int err; @@ -88,7 +87,7 @@ static ssize_t module_write(const char __user *buf, size_t len, } static ssize_t register_write(struct file *file, const char __user *buf, - size_t len, loff_t *ppos) + size_t len, loff_t *ppos) { return module_write(buf, len, ppos, 1); } @@ -98,7 +97,7 @@ static const struct file_operations fops_register = { }; static ssize_t unregister_write(struct file *file, const char __user *buf, - size_t len, loff_t *ppos) + size_t len, loff_t *ppos) { return module_write(buf, len, ppos, 0); } From da0cbb43172bcee2e3ee35de52d70da2b1d2f568 Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Thu, 26 Mar 2026 10:27:11 +0800 Subject: [PATCH 084/110] lua-lsm: auxlib: add forward declarations Signed-off-by: Zongyao Chen Discussed-in: Merge-Request 26537804 2/3, URL: https://code.alibaba-inc.com/opensrc/linux/codereview/26537804 --- security/lua/auxlib.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/security/lua/auxlib.h b/security/lua/auxlib.h index 657785ebb964..113016257488 100644 --- a/security/lua/auxlib.h +++ b/security/lua/auxlib.h @@ -11,6 +11,10 @@ #include #include +struct cred; +struct dentry; +struct file; + static inline gfp_t lua_lsm_gfp(void) { /* From b40a8ba794c13ef602bfd3b805928507ed29df40 Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Thu, 26 Mar 2026 10:03:28 +0800 Subject: [PATCH 085/110] lua-lsm: auxlib: guard optional path buffer free Signed-off-by: Zongyao Chen Discussed-in: Merge-Request 26537804 3/3, URL: https://code.alibaba-inc.com/opensrc/linux/codereview/26537804 --- security/lua/auxlib.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/security/lua/auxlib.c b/security/lua/auxlib.c index f39389a98e42..d1667d5ec26b 100644 --- a/security/lua/auxlib.c +++ b/security/lua/auxlib.c @@ -438,7 +438,8 @@ int aux_file_path(lua_State *L, struct file *filp) lua_pushstring(L, path); nres = 1; } - kfree(buf); + if (buf) + kfree(buf); return nres; } @@ -473,7 +474,8 @@ int aux_dentry_path(lua_State *L, struct dentry *dentry, int rawpath) lua_pushstring(L, path); nres = 1; } - kfree(buf); + if (buf) + kfree(buf); return nres; } From fb90bbfd0209b164184d36a166800babb1f27b9a Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Mon, 30 Mar 2026 14:24:44 +0800 Subject: [PATCH 086/110] lua-lsm: rename debug symbol to avoid allyesconfig conflict Signed-off-by: Zongyao Chen --- security/lua/debug.h | 4 ++-- security/lua/lsm.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/security/lua/debug.h b/security/lua/debug.h index 4239e4232891..016a49a3e6ba 100644 --- a/security/lua/debug.h +++ b/security/lua/debug.h @@ -14,11 +14,11 @@ #include #include -extern bool debug; +extern bool lua_lsm_debug; #define __log_level(level, fmt, ...) \ do { \ - if (debug) \ + if (lua_lsm_debug) \ pr_ ## level("%s [%d]: %d %s - " fmt, \ current->comm, task_pid_nr(current), \ __LINE__, __func__, ##__VA_ARGS__); \ diff --git a/security/lua/lsm.c b/security/lua/lsm.c index b701ab6e5402..b9ceeba0d8cb 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -29,11 +29,11 @@ #ifdef DEBUG -bool debug = true; +bool lua_lsm_debug = true; static int __init disable_debug(char *str) { - debug = false; + lua_lsm_debug = false; return 1; } __setup("lua.nodebug", disable_debug); From cd33b8172558a56c08e4af1321c94f2084ecf61f Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Wed, 1 Apr 2026 14:23:19 +0800 Subject: [PATCH 087/110] lib: expose LUA in Kconfig --- lib/Kconfig | 12 ++++++++++-- security/lua/Kconfig | 4 ++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/Kconfig b/lib/Kconfig index af647befcbb2..7885e1f826e3 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -654,10 +654,18 @@ config ARCH_HAS_SETJMP bool config SETJMP_ARCH - tristate + bool depends on ARCH_HAS_SETJMP config LUA - tristate + bool "Lua support" + depends on ARCH_HAS_SETJMP default n select SETJMP_ARCH + help + Build the in-kernel Lua core library for built-in users. + + This is required by subsystems that embed a Lua VM, such as + SECURITY_LUA_LSM. + + If unsure, say N. diff --git a/security/lua/Kconfig b/security/lua/Kconfig index 1df27b3fc643..531ce7d00016 100644 --- a/security/lua/Kconfig +++ b/security/lua/Kconfig @@ -3,10 +3,10 @@ config SECURITY_LUA_LSM bool "Lua based LSM Support" depends on SECURITY - select LUA + depends on LUA default n help - This selects the Lua based LSM. + This enables the Lua based LSM. If you are unsure how to answer this question, answer N. config SECURITY_LUA_LSM_STATS From 12a7d0ebfbfd4d2f48c96b2d8b158e50cf240050 Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Wed, 1 Apr 2026 14:23:19 +0800 Subject: [PATCH 088/110] docs: update Lua-LSM config docs --- security/lua/docs/README.md | 1 + security/lua/docs/USAGE.md | 1 + 2 files changed, 2 insertions(+) diff --git a/security/lua/docs/README.md b/security/lua/docs/README.md index d8f183ef357d..2abdbd8f53e6 100644 --- a/security/lua/docs/README.md +++ b/security/lua/docs/README.md @@ -22,6 +22,7 @@ local logo = [==[ 1) Enable kernel config options: +- `CONFIG_LUA=y` - `CONFIG_SECURITY_LUA_LSM=y` - Optional: `CONFIG_SECURITY_LUA_LSM_STATS=y` - Optional: `CONFIG_SECURITY_LUA_LSM_DEBUG=y` diff --git a/security/lua/docs/USAGE.md b/security/lua/docs/USAGE.md index 5c22c31d1155..32fdfd580baa 100644 --- a/security/lua/docs/USAGE.md +++ b/security/lua/docs/USAGE.md @@ -2,6 +2,7 @@ ## Build and enable +- Enable `CONFIG_LUA`. - Enable `CONFIG_SECURITY_LUA_LSM`. - Include `lua` in the LSM order (CONFIG_LSM or kernel cmdline `lsm=`). - Optional: From 6ef700d9a37a214a49e89fe9e986f5ba457db87b Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Thu, 9 Apr 2026 17:29:15 +0800 Subject: [PATCH 089/110] lua-lsm: guard blob-backed object access on rollback Signed-off-by: Zongyao Chen --- security/lua/kvcache.c | 9 ++++++ security/lua/lsm.h | 12 ++++++++ security/lua/lsm_defs.c | 64 +++++++++++++++++++++++++++++++++++++++ security/lua/lua_object.h | 2 +- 4 files changed, 86 insertions(+), 1 deletion(-) diff --git a/security/lua/kvcache.c b/security/lua/kvcache.c index 8ac8c9438a41..89c4627fadaf 100644 --- a/security/lua/kvcache.c +++ b/security/lua/kvcache.c @@ -513,6 +513,9 @@ int lua_object_get(lua_State *L, struct kvcache_dict *dict) { struct lua_lsm_module *module; + if (!dict) + return 0; + module = module_from_object_fenv(L, 1); if (!module) return 0; @@ -523,6 +526,9 @@ int lua_object_incr(lua_State *L, struct kvcache_dict *dict) { struct lua_lsm_module *module; + if (!dict) + return kvcache_result(L, -ESRCH); + module = module_from_object_fenv(L, 1); if (!module) return kvcache_result(L, -ESRCH); @@ -575,6 +581,9 @@ int lua_object_newindex(lua_State *L, struct kvcache_dict *dict) { struct lua_lsm_module *module; + if (!dict) + return kvcache_result(L, -ESRCH); + module = module_from_object_fenv(L, 1); if (!module) return kvcache_result(L, -ESRCH); diff --git a/security/lua/lsm.h b/security/lua/lsm.h index 52a66cdd3484..88932b6303a6 100644 --- a/security/lua/lsm.h +++ b/security/lua/lsm.h @@ -141,11 +141,15 @@ struct lua_lsm_object { static inline struct lua_lsm_object *lua_lsm_cred(const struct cred *cred) { + if (unlikely(!cred->security)) + return NULL; return cred->security + lua_lsm_blob_sizes.lbs_cred; } static inline struct lua_lsm_object *lua_lsm_file(const struct file *file) { + if (unlikely(!file->f_security)) + return NULL; return file->f_security + lua_lsm_blob_sizes.lbs_file; } @@ -163,6 +167,8 @@ static inline struct lua_lsm_object *lua_lsm_inode(const struct inode *inode) static inline struct lua_lsm_object *lua_lsm_inode_rcu(void *inode_security) { + if (unlikely(!inode_security)) + return NULL; return inode_security + lua_lsm_blob_sizes.lbs_inode; } @@ -173,11 +179,15 @@ static inline struct lua_lsm_object *lua_lsm_sock(const struct sock *sock) static inline struct lua_lsm_object *lua_lsm_superblock(const struct super_block *superblock) { + if (unlikely(!superblock->s_security)) + return NULL; return superblock->s_security + lua_lsm_blob_sizes.lbs_superblock; } static inline struct lua_lsm_object *lua_lsm_ipc(const struct kern_ipc_perm *ipc) { + if (unlikely(!ipc->security)) + return NULL; return ipc->security + lua_lsm_blob_sizes.lbs_ipc; } @@ -188,6 +198,8 @@ static inline struct lua_lsm_object *lua_lsm_key(const struct key *key) static inline struct lua_lsm_object *lua_lsm_msgmsg(const struct msg_msg *msg) { + if (unlikely(!msg->security)) + return NULL; return msg->security + lua_lsm_blob_sizes.lbs_msg_msg; } diff --git a/security/lua/lsm_defs.c b/security/lua/lsm_defs.c index b2f38ece7086..3e09dfd43978 100644 --- a/security/lua/lsm_defs.c +++ b/security/lua/lsm_defs.c @@ -346,6 +346,10 @@ LUA_LSM_INT_DEFINE2(fs_context_parse_param, struct fs_context *, fc, LUA_LSM_PREPARE_DEFINE1(sb_alloc_security, struct super_block *, sb) { struct lua_lsm_object *llo = lua_lsm_superblock(sb); + + if (!llo) + return -EINVAL; + kvcache_dict_init(&llo->dict); return 0; } @@ -374,6 +378,10 @@ LUA_LSM_VOID_DEFINE1(sb_delete, struct super_block *, sb) LUA_LSM_POSTPONE_DEFINE1(sb_free_security, struct super_block *, sb) { struct lua_lsm_object *llo = lua_lsm_superblock(sb); + + if (!llo) + return; + kvcache_dict_free(&llo->dict); } @@ -717,6 +725,10 @@ LUA_LSM_INT_DEFINE3(path_notify, const struct path *, path, LUA_LSM_PREPARE_DEFINE1(inode_alloc_security, struct inode *, inode) { struct lua_lsm_object *llo = lua_lsm_inode(inode); + + if (!llo) + return -EINVAL; + kvcache_dict_init(&llo->dict); return 0; } @@ -1396,6 +1408,10 @@ LUA_LSM_INT_DEFINE2(file_permission, struct file *, file, int, mask) LUA_LSM_PREPARE_DEFINE1(file_alloc_security, struct file *, file) { struct lua_lsm_object *llo = lua_lsm_file(file); + + if (!llo) + return -EINVAL; + kvcache_dict_init(&llo->dict); return 0; } @@ -1424,6 +1440,10 @@ LUA_LSM_VOID_DEFINE1(file_release, struct file *, file) LUA_LSM_POSTPONE_DEFINE1(file_free_security, struct file *, file) { struct lua_lsm_object *llo = lua_lsm_file(file); + + if (!llo) + return; + kvcache_dict_free(&llo->dict); } @@ -1628,6 +1648,10 @@ LUA_LSM_VOID_DEFINE1(task_free, struct task_struct *, task) LUA_LSM_PREPARE_DEFINE2(cred_alloc_blank, struct cred *, cred, gfp_t, gfp) { struct lua_lsm_object *llo = lua_lsm_cred(cred); + + if (!llo) + return -EINVAL; + kvcache_dict_init(&llo->dict); return 0; } @@ -1648,6 +1672,10 @@ LUA_LSM_INT_DEFINE2(cred_alloc_blank, struct cred *, cred, gfp_t, gfp) LUA_LSM_POSTPONE_DEFINE1(cred_free, struct cred *, cred) { struct lua_lsm_object *llo = lua_lsm_cred(cred); + + if (!llo) + return; + kvcache_dict_free(&llo->dict); } @@ -1667,6 +1695,10 @@ LUA_LSM_PREPARE_DEFINE3(cred_prepare, struct cred *, new, const struct cred *, old, gfp_t, gfp) { struct lua_lsm_object *llo = lua_lsm_cred(new); + + if (!llo) + return -EINVAL; + kvcache_dict_init(&llo->dict); return 0; } @@ -2053,6 +2085,10 @@ LUA_LSM_VOID_DEFINE2(ipc_getlsmprop, struct kern_ipc_perm *, ipcp, LUA_LSM_PREPARE_DEFINE1(msg_msg_alloc_security, struct msg_msg *, msg) { struct lua_lsm_object *llo = lua_lsm_msgmsg(msg); + + if (!llo) + return -EINVAL; + kvcache_dict_init(&llo->dict); return 0; } @@ -2072,6 +2108,10 @@ LUA_LSM_INT_DEFINE1(msg_msg_alloc_security, struct msg_msg *, msg) LUA_LSM_POSTPONE_DEFINE1(msg_msg_free_security, struct msg_msg *, msg) { struct lua_lsm_object *llo = lua_lsm_msgmsg(msg); + + if (!llo) + return; + kvcache_dict_free(&llo->dict); } @@ -2090,6 +2130,10 @@ LUA_LSM_VOID_DEFINE1(msg_msg_free_security, struct msg_msg *, msg) LUA_LSM_PREPARE_DEFINE1(msg_queue_alloc_security, struct kern_ipc_perm *, perm) { struct lua_lsm_object *llo = lua_lsm_ipc(perm); + + if (!llo) + return -EINVAL; + kvcache_dict_init(&llo->dict); return 0; } @@ -2109,6 +2153,10 @@ LUA_LSM_INT_DEFINE1(msg_queue_alloc_security, struct kern_ipc_perm *, perm) LUA_LSM_POSTPONE_DEFINE1(msg_queue_free_security, struct kern_ipc_perm *, perm) { struct lua_lsm_object *llo = lua_lsm_ipc(perm); + + if (!llo) + return; + kvcache_dict_free(&llo->dict); } @@ -2175,6 +2223,10 @@ LUA_LSM_INT_DEFINE5(msg_queue_msgrcv, struct kern_ipc_perm *, perm, LUA_LSM_PREPARE_DEFINE1(shm_alloc_security, struct kern_ipc_perm *, perm) { struct lua_lsm_object *llo = lua_lsm_ipc(perm); + + if (!llo) + return -EINVAL; + kvcache_dict_init(&llo->dict); return 0; } @@ -2194,6 +2246,10 @@ LUA_LSM_INT_DEFINE1(shm_alloc_security, struct kern_ipc_perm *, perm) LUA_LSM_POSTPONE_DEFINE1(shm_free_security, struct kern_ipc_perm *, perm) { struct lua_lsm_object *llo = lua_lsm_ipc(perm); + + if (!llo) + return; + kvcache_dict_free(&llo->dict); } @@ -2244,6 +2300,10 @@ LUA_LSM_INT_DEFINE3(shm_shmat, struct kern_ipc_perm *, perm, LUA_LSM_PREPARE_DEFINE1(sem_alloc_security, struct kern_ipc_perm *, perm) { struct lua_lsm_object *llo = lua_lsm_ipc(perm); + + if (!llo) + return -EINVAL; + kvcache_dict_init(&llo->dict); return 0; } @@ -2263,6 +2323,10 @@ LUA_LSM_INT_DEFINE1(sem_alloc_security, struct kern_ipc_perm *, perm) LUA_LSM_POSTPONE_DEFINE1(sem_free_security, struct kern_ipc_perm *, perm) { struct lua_lsm_object *llo = lua_lsm_ipc(perm); + + if (!llo) + return; + kvcache_dict_free(&llo->dict); } diff --git a/security/lua/lua_object.h b/security/lua/lua_object.h index 5cee13e817a0..5d7f26cbb2db 100644 --- a/security/lua/lua_object.h +++ b/security/lua/lua_object.h @@ -53,7 +53,7 @@ { \ ctype p = toraw ## name(L, 1); \ struct lua_lsm_ ## blob *ll = lua_lsm_ ## name(p); \ - return lua_object_ ## fname(L, &ll->dict); \ + return lua_object_ ## fname(L, ll ? &ll->dict : NULL); \ } #define LUA_OBJECT_FUNCS_DEFINE(name, ctype, has_kvcache) \ From 295eb0550d2d5b9d216c479ee6c26ec966da7fb9 Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Thu, 9 Apr 2026 17:29:15 +0800 Subject: [PATCH 090/110] lua-lsm: Correct header file pach Signed-off-by: Zongyao Chen --- security/lua/lua_object.h | 1 + 1 file changed, 1 insertion(+) diff --git a/security/lua/lua_object.h b/security/lua/lua_object.h index 5d7f26cbb2db..d7588acc1815 100644 --- a/security/lua/lua_object.h +++ b/security/lua/lua_object.h @@ -8,6 +8,7 @@ #ifndef _SECURITY_LUA_LSM_LUA_OBJECT_H #define _SECURITY_LUA_LSM_LUA_OBJECT_H +#include #include "auxlib.h" #include "lsm.h" #include "kvcache.h" From c47e27281007977dcc09e53e943e965a7cf8d70d Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Thu, 16 Apr 2026 10:04:42 +0800 Subject: [PATCH 091/110] lua-lsm: move hook stats accounting to percpu Signed-off-by: Zongyao Chen --- security/lua/lsm.c | 84 ++++++++++++++++++++++++++++++++++++----- security/lua/lsm.h | 7 +--- security/lua/lsm_defs.h | 17 +++++---- 3 files changed, 87 insertions(+), 21 deletions(-) diff --git a/security/lua/lsm.c b/security/lua/lsm.c index b9ceeba0d8cb..9b7f4dc981d5 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -16,6 +16,7 @@ #include #include #include +#include #include /* for __MAP */ #include /* for ktime_get */ #include @@ -78,6 +79,19 @@ static atomic_t vm_nusage = ATOMIC_INIT(0); #ifdef CONFIG_SECURITY_LUA_LSM_STATS +struct lua_lsm_hook_pcpu_stat { + u64_stats_t count; + u64_stats_t time; + u64_stats_t maxtime; + struct u64_stats_sync syncp; +}; + +struct lua_lsm_pcpu_stats { + struct lua_lsm_hook_pcpu_stat hooks[__LL_NR_MAX]; +}; + +static DEFINE_PER_CPU(struct lua_lsm_pcpu_stats, lua_lsm_pcpu_stats); + static atomic_t vm_nalloc = ATOMIC_INIT(0); static atomic_t vm_nfree = ATOMIC_INIT(0); static atomic64_t mem_nalloc = ATOMIC64_INIT(0); @@ -87,6 +101,33 @@ static atomic_t mem_total = ATOMIC_INIT(0); static atomic_t mem_minimum = ATOMIC_INIT(INT_MAX); static atomic_t mem_maximum = ATOMIC_INIT(0); +static void lua_lsm_hook_stats_init_cpu(int cpu) +{ + struct lua_lsm_pcpu_stats *stats = per_cpu_ptr(&lua_lsm_pcpu_stats, cpu); + int i; + + for (i = 0; i < __LL_NR_MAX; i++) + u64_stats_init(&stats->hooks[i].syncp); +} + +void lua_lsm_hook_stats_record(unsigned int nr, u64 delta) +{ + struct lua_lsm_hook_pcpu_stat *stat; + unsigned long flags; + u64 maxtime; + + local_irq_save(flags); + stat = &this_cpu_ptr(&lua_lsm_pcpu_stats)->hooks[nr]; + u64_stats_update_begin(&stat->syncp); + u64_stats_inc(&stat->count); + u64_stats_add(&stat->time, delta); + maxtime = u64_stats_read(&stat->maxtime); + if (maxtime < delta) + u64_stats_set(&stat->maxtime, delta); + u64_stats_update_end(&stat->syncp); + local_irq_restore(flags); +} + static void lvm_stats_vmalloc(void) { atomic_inc(&vm_nalloc); @@ -156,22 +197,42 @@ void lvm_stats_show(struct seq_file *m) int lsm_funcs_show(struct seq_file *m, void *v) { - struct lua_lsm_hook_stat *stat; - int i = 1; + int i; + int cpu; seq_printf(m, "stats for lua-lsm (ns)\n"); seq_printf(m, "%3s %-28s %4s %12s %15s %10s %12s\n", "num", "name", "nlsm", "count", "total", "average", "maxtime"); seq_printf(m, "%s\n", TABLINE); - for (stat = lua_lsm_hook_stats; stat->name; stat++) { - int n = atomic_read(&stat->count); - s64 total = atomic64_read(&stat->time); - s64 maxtime = atomic64_read(&stat->maxtime); + for (i = 0; lua_lsm_hook_stats[i].name; i++) { + u64 count = 0; + u64 total = 0; + u64 maxtime = 0; - seq_printf(m, "%3d %-28s %4d %12d %15llu %10llu %12llu\n", - i++, stat->name, atomic_read(&stat->nhooks), - n, total, n ? total / n : 0, maxtime); + for_each_possible_cpu(cpu) { + const struct lua_lsm_hook_pcpu_stat *stat; + unsigned int start; + u64 tcount, ttotal, tmaxtime; + + stat = &per_cpu_ptr(&lua_lsm_pcpu_stats, cpu)->hooks[i]; + do { + start = u64_stats_fetch_begin(&stat->syncp); + tcount = u64_stats_read(&stat->count); + ttotal = u64_stats_read(&stat->time); + tmaxtime = u64_stats_read(&stat->maxtime); + } while (u64_stats_fetch_retry(&stat->syncp, start)); + + count += tcount; + total += ttotal; + if (maxtime < tmaxtime) + maxtime = tmaxtime; + } + + seq_printf(m, "%3d %-28s %4d %12llu %15llu %10llu %12llu\n", + i + 1, lua_lsm_hook_stats[i].name, + atomic_read(&lua_lsm_hook_stats[i].nhooks), + count, total, count ? total / count : 0, maxtime); } return 0; } @@ -1299,6 +1360,11 @@ static int __init lua_lsm_init(void) for_each_possible_cpu(cpu) lvm_pool_init_cpu(cpu); +#ifdef CONFIG_SECURITY_LUA_LSM_STATS + for_each_possible_cpu(cpu) + lua_lsm_hook_stats_init_cpu(cpu); +#endif + err = task_blob_init(current); if (err) return err; diff --git a/security/lua/lsm.h b/security/lua/lsm.h index 88932b6303a6..55dc3001e12d 100644 --- a/security/lua/lsm.h +++ b/security/lua/lsm.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include "bitmap.h" #include "kvcache.h" @@ -28,11 +29,6 @@ extern int lua_lsm_initialized __initdata; struct lua_lsm_hook_stat { const char *name; atomic_t nhooks; -#ifdef CONFIG_SECURITY_LUA_LSM_STATS - atomic_t count; - atomic64_t time; /* ns */ - atomic64_t maxtime; -#endif }; extern struct lua_lsm_hook_stat lua_lsm_hook_stats[]; @@ -106,6 +102,7 @@ int lua_lsm_module_unregister(const char *name); int modules_show(struct seq_file *m, void *v); #ifdef CONFIG_SECURITY_LUA_LSM_STATS +void lua_lsm_hook_stats_record(unsigned int nr, u64 delta); void lvm_stats_show(struct seq_file *m); int lsm_funcs_show(struct seq_file *m, void *v); #endif diff --git a/security/lua/lsm_defs.h b/security/lua/lsm_defs.h index 7ed42b22a3e9..d6762e9b1a8a 100644 --- a/security/lua/lsm_defs.h +++ b/security/lua/lsm_defs.h @@ -22,21 +22,23 @@ #ifdef CONFIG_SECURITY_LUA_LSM_STATS +#define DECLARE_STATS_VARS() \ + u64 ___stats_start = 0 + #define START_STATS(NAME) \ do { \ - ktime_t ___start = ktime_get(); \ - s64 ___delta; \ - atomic_inc(&lua_lsm_hook_stats[__LL_NR_ ## NAME].count); + ___stats_start = ktime_get_ns(); \ + } while (0) #define END_STATS(NAME) \ - ___delta = ktime_to_ns(ktime_get()) - ktime_to_ns(___start); \ - if (atomic64_read(&lua_lsm_hook_stats[__LL_NR_ ## NAME].maxtime) < ___delta) \ - atomic64_set(&lua_lsm_hook_stats[__LL_NR_ ## NAME].maxtime, ___delta); \ - atomic64_add(___delta, &lua_lsm_hook_stats[__LL_NR_ ## NAME].time); \ + do { \ + lua_lsm_hook_stats_record(__LL_NR_ ## NAME, \ + ktime_get_ns() - ___stats_start); \ } while (0) #else +#define DECLARE_STATS_VARS() #define START_STATS(NAME) #define END_STATS(NAME) @@ -171,6 +173,7 @@ { \ int idx; \ int ret; \ + DECLARE_STATS_VARS(); \ START_STATS(NAME); \ idx = srcu_read_lock(&modules_ss); \ ret = __prepare_ ## NAME(__MAP(x, __SC_ARGS, __VA_ARGS__)); \ From fdfff2592a0f86df46e4ee036dcd1582122ff3b2 Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Tue, 21 Apr 2026 10:59:27 +0800 Subject: [PATCH 092/110] lua-lsm: make lvm pool size configurable Signed-off-by: Zongyao Chen --- security/lua/lsm.c | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/security/lua/lsm.c b/security/lua/lsm.c index 9b7f4dc981d5..6deb2fe75042 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -10,6 +10,7 @@ #include "debug.h" #include #include +#include #include #include #include @@ -263,7 +264,36 @@ static DEFINE_PER_CPU(struct lvm_state *, irq_lvms); static int lua_state_alloc(struct lvm_state *lvm); static void lua_state_free(struct lvm_state *lvm); -#define LVM_POOL_MAX 8 +#define LUA_LVM_POOL_MAX_DEFAULT 8 +#define LUA_LVM_POOL_MAX_LIMIT 256 + +static unsigned int lua_lvm_pool_max __read_mostly = LUA_LVM_POOL_MAX_DEFAULT; + +static int __init lua_lvm_pool_max_setup(char *str) +{ + unsigned int val; + int err; + + if (!str || !*str) + return 1; + + err = kstrtouint(str, 10, &val); + if (err) { + pr_warn("invalid lua.lvm_pool_max='%s', keeping %u\n", + str, lua_lvm_pool_max); + return 1; + } + + if (val > LUA_LVM_POOL_MAX_LIMIT) { + pr_warn("lua.lvm_pool_max=%u exceeds limit %u, keeping %u\n", + val, LUA_LVM_POOL_MAX_LIMIT, lua_lvm_pool_max); + return 1; + } + + WRITE_ONCE(lua_lvm_pool_max, val); + return 1; +} +__setup("lua.lvm_pool_max=", lua_lvm_pool_max_setup); struct lvm_pool_cpu { struct lvm_state *head; @@ -318,7 +348,7 @@ static void lvm_pool_put(struct lvm_state *lvm) cpu = get_cpu(); pool = &per_cpu(lvm_pools, cpu); raw_spin_lock_irqsave(&pool->lock, flags); - if (pool->count < LVM_POOL_MAX) { + if (pool->count < READ_ONCE(lua_lvm_pool_max)) { lvm->next = pool->head; pool->head = lvm; pool->count++; @@ -1386,7 +1416,8 @@ static int __init lua_lsm_init(void) /* Report that Lua-LSM successfully initialized */ lua_lsm_initialized = 1; - pr_info("Lua based LSM initialized\n"); + pr_info("Lua based LSM initialized (lvm_pool_max=%u)\n", + READ_ONCE(lua_lvm_pool_max)); return 0; } From da2d7ac46a68c455b9d705bd6beb2526d37c925e Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Tue, 21 Apr 2026 10:59:44 +0800 Subject: [PATCH 093/110] docs: document Lua-LSM lvm pool cmdline Signed-off-by: Zongyao Chen --- security/lua/docs/USAGE.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/security/lua/docs/USAGE.md b/security/lua/docs/USAGE.md index 32fdfd580baa..8121b51a9dca 100644 --- a/security/lua/docs/USAGE.md +++ b/security/lua/docs/USAGE.md @@ -41,6 +41,24 @@ Check version: cat /sys/kernel/security/lua/version ``` +## Boot-time tuning + +The per-CPU Lua VM pool size is controlled by the kernel command line +parameter `lua.lvm_pool_max=`. The default is `8`. + +Examples: + +``` +lua.lvm_pool_max=64 +lua.lvm_pool_max=0 +``` + +Notes: + +- `0` disables returning Lua VMs to the per-CPU pool. +- The current implementation accepts decimal values in the range `0..256`. +- This is a boot-time setting only; there is no runtime knob in securityfs. + ## Permissions Loading and unloading modules requires `CAP_MAC_ADMIN`. From 8ce60df38692d1f1765c97c64c8cb57be18b6776 Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Tue, 21 Apr 2026 12:32:51 +0800 Subject: [PATCH 094/110] lua-lsm: set default lvm pool size to 32 Signed-off-by: Zongyao Chen --- security/lua/docs/USAGE.md | 2 +- security/lua/lsm.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/security/lua/docs/USAGE.md b/security/lua/docs/USAGE.md index 8121b51a9dca..96499eba2793 100644 --- a/security/lua/docs/USAGE.md +++ b/security/lua/docs/USAGE.md @@ -44,7 +44,7 @@ cat /sys/kernel/security/lua/version ## Boot-time tuning The per-CPU Lua VM pool size is controlled by the kernel command line -parameter `lua.lvm_pool_max=`. The default is `8`. +parameter `lua.lvm_pool_max=`. The default is `32`. Examples: diff --git a/security/lua/lsm.c b/security/lua/lsm.c index 6deb2fe75042..7122f3ef30fc 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -264,7 +264,7 @@ static DEFINE_PER_CPU(struct lvm_state *, irq_lvms); static int lua_state_alloc(struct lvm_state *lvm); static void lua_state_free(struct lvm_state *lvm); -#define LUA_LVM_POOL_MAX_DEFAULT 8 +#define LUA_LVM_POOL_MAX_DEFAULT 32 #define LUA_LVM_POOL_MAX_LIMIT 256 static unsigned int lua_lvm_pool_max __read_mostly = LUA_LVM_POOL_MAX_DEFAULT; From 116963309163f6c803e36d55fb108c39e8abeff9 Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Thu, 23 Apr 2026 09:44:56 +0800 Subject: [PATCH 095/110] lua-lsm: stop registering legacy procattr hooks Generic /proc//attr/* uses legacy single-owner lsm dispatch. When Lua-LSM registers getprocattr/setprocattr ahead of SELinux, procattr operations are routed to Lua first and fail with -EINVAL. Teach Lua-LSM to treat getprocattr and setprocattr as unsupported, and skip registering them in lua_lsm_init(). This leaves the legacy procattr ABI to the LSMs that actually implement it. Signed-off-by: Zongyao Chen --- security/lua/lsm.c | 8 +++++++- security/lua/lsm.h | 11 +++++++++++ security/lua/lsm_defs.c | 9 +-------- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/security/lua/lsm.c b/security/lua/lsm.c index 7122f3ef30fc..3195dc04aa21 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -1386,6 +1386,7 @@ static int __init lua_lsm_init(void) struct lvm_state *lvm; int cpu; int err; + int i; for_each_possible_cpu(cpu) lvm_pool_init_cpu(cpu); @@ -1411,7 +1412,12 @@ static int __init lua_lsm_init(void) per_cpu(irq_lvms, cpu) = lvm; } - security_add_hooks(lua_lsm_hooks, ARRAY_SIZE(lua_lsm_hooks), &lua_lsmid); + /* Register only the hooks that Lua-LSM exposes to modules. */ + for (i = 0; i < ARRAY_SIZE(lua_lsm_hooks); i++) { + if (!lua_lsm_hook_supported(i)) + continue; + security_add_hooks(&lua_lsm_hooks[i], 1, &lua_lsmid); + } /* Report that Lua-LSM successfully initialized */ lua_lsm_initialized = 1; diff --git a/security/lua/lsm.h b/security/lua/lsm.h index 55dc3001e12d..04e0434e50e3 100644 --- a/security/lua/lsm.h +++ b/security/lua/lsm.h @@ -47,6 +47,17 @@ enum { __LL_NR_MAX }; +static inline bool lua_lsm_hook_supported(unsigned int nr) +{ + switch (nr) { + case __LL_NR_getprocattr: + case __LL_NR_setprocattr: + return false; + default: + return nr < __LL_NR_MAX; + } +} + struct lua_lsm_module_shdict { struct list_head list; struct kvcache_dict dict; diff --git a/security/lua/lsm_defs.c b/security/lua/lsm_defs.c index 3e09dfd43978..4326c13dec3d 100644 --- a/security/lua/lsm_defs.c +++ b/security/lua/lsm_defs.c @@ -2419,10 +2419,7 @@ LUA_LSM_INT_DEFINE4(setselfattr, unsigned int, attr, struct lsm_ctx *, ctx, lua_pushinteger(L, (lua_Integer)flags); } -/** - * TODO: getprocattr - * Default: -EINVAL - */ +/* Not registered by Lua-LSM. */ LUA_LSM_INT_DEFINE3(getprocattr, struct task_struct *, p, const char *, name, char **, value) { @@ -2431,10 +2428,6 @@ LUA_LSM_INT_DEFINE3(getprocattr, struct task_struct *, p, lua_pushnil(L); /* TODO: value */ } -/** - * setprocattr - * Default: -EINVAL - */ LUA_LSM_INT_DEFINE3(setprocattr, const char *, name, void *, value, size_t, size) { From 05260f93f4e62bb99be6cb583b2b1f59cd4b5ae8 Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Thu, 23 Apr 2026 14:27:03 +0800 Subject: [PATCH 096/110] lua-lsm: do not register lsmprop_to_secctx security_lsmprop_to_secctx() uses the first registered LSM when called with LSM_ID_UNDEF. audit_log_subj_ctx() takes that path in the single-provider case. Lua-LSM does not implement lsmprop_to_secctx yet and currently falls back to the default -EOPNOTSUPP handler. When Lua is ordered before SELinux, audit reaches Lua first and emits repeated "audit: error in audit_log_subj_ctx" messages. Mark lsmprop_to_secctx unsupported and do not register it for Lua-LSM for now. This leaves secctx conversion to LSMs that implement it. Signed-off-by: Zongyao Chen --- security/lua/lsm.h | 1 + 1 file changed, 1 insertion(+) diff --git a/security/lua/lsm.h b/security/lua/lsm.h index 04e0434e50e3..a575b4608326 100644 --- a/security/lua/lsm.h +++ b/security/lua/lsm.h @@ -52,6 +52,7 @@ static inline bool lua_lsm_hook_supported(unsigned int nr) switch (nr) { case __LL_NR_getprocattr: case __LL_NR_setprocattr: + case __LL_NR_lsmprop_to_secctx: return false; default: return nr < __LL_NR_MAX; From 85d88b50a2159f158d6f94339b9c5fb690082529 Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Thu, 23 Apr 2026 20:13:06 +0800 Subject: [PATCH 097/110] lua-lsm: hide unsupported hooks from modules and introspection Lua-LSM should not advertise hooks that it does not support or register. Reject unsupported hook names during module loading, and omit them from kernel.lsm_funcs() and /sys/kernel/security/lua/lsm_funcs. Signed-off-by: Zongyao Chen --- security/lua/lsm.c | 13 ++++++++++++- security/lua/lua_kernel.c | 16 +++++++++++++--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/security/lua/lsm.c b/security/lua/lsm.c index 3195dc04aa21..7b34439f0fae 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -199,6 +199,7 @@ void lvm_stats_show(struct seq_file *m) int lsm_funcs_show(struct seq_file *m, void *v) { int i; + int n = 0; int cpu; seq_printf(m, "stats for lua-lsm (ns)\n"); @@ -211,6 +212,9 @@ int lsm_funcs_show(struct seq_file *m, void *v) u64 total = 0; u64 maxtime = 0; + if (!lua_lsm_hook_supported(i)) + continue; + for_each_possible_cpu(cpu) { const struct lua_lsm_hook_pcpu_stat *stat; unsigned int start; @@ -231,7 +235,7 @@ int lsm_funcs_show(struct seq_file *m, void *v) } seq_printf(m, "%3d %-28s %4d %12llu %15llu %10llu %12llu\n", - i + 1, lua_lsm_hook_stats[i].name, + ++n, lua_lsm_hook_stats[i].name, atomic_read(&lua_lsm_hook_stats[i].nhooks), count, total, count ? total / count : 0, maxtime); } @@ -940,6 +944,13 @@ int lua_lsm_module_register(const char *code, size_t len) if (strcmp(lua_lsm_hook_stats[i].name, key) != 0) continue; + if (!lua_lsm_hook_supported(i)) { + __log_err("hook '%s' is not supported by Lua-LSM\n", + key); + err = -EOPNOTSUPP; + goto err_free_module; + } + if (!lua_isfunction(L, -1)) { __log_err("field '%s' must be a function\n", key); break; diff --git a/security/lua/lua_kernel.c b/security/lua/lua_kernel.c index 2507c3e659ac..ee3544384682 100644 --- a/security/lua/lua_kernel.c +++ b/security/lua/lua_kernel.c @@ -411,9 +411,19 @@ static int kernel_lsm_funcs(lua_State *L) #undef LSM_HOOK }; int i; + int n = 0; - lua_createtable(L, ARRAY_SIZE(lsm_funcs), 0); for (i = 0; i < ARRAY_SIZE(lsm_funcs); i++) { + if (lua_lsm_hook_supported(i)) + n++; + } + + lua_createtable(L, n, 0); + n = 0; + for (i = 0; i < ARRAY_SIZE(lsm_funcs); i++) { + if (!lua_lsm_hook_supported(i)) + continue; + lua_createtable(L, 3, 0); lua_pushstring(L, lsm_funcs[i].funcname); lua_rawseti(L, -2, 1); @@ -421,8 +431,8 @@ static int kernel_lsm_funcs(lua_State *L) lua_rawseti(L, -2, 2); lua_pushinteger(L, lsm_funcs[i].nargs); lua_rawseti(L, -2, 3); - /* res[i + 1] = { funcname, rtype, nargs } */ - lua_rawseti(L, -2, i + 1); + /* res[n + 1] = { funcname, rtype, nargs } */ + lua_rawseti(L, -2, ++n); } return 1; } From 2a921f77770d2a5f46723a6ac8d40dac3a6aa2ad Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Thu, 23 Apr 2026 20:13:33 +0800 Subject: [PATCH 098/110] docs: describe Lua-LSM supported hook surface Document that Lua-LSM exposes only its supported hook set through module APIs and observability output, and call out that getprocattr, setprocattr, and lsmprop_to_secctx are not part of that surface. Signed-off-by: Zongyao Chen --- security/lua/docs/API.md | 8 +++++++- security/lua/docs/OBSERVABILITY.md | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/security/lua/docs/API.md b/security/lua/docs/API.md index 695ca56dd700..be5a8a0bfb26 100644 --- a/security/lua/docs/API.md +++ b/security/lua/docs/API.md @@ -11,7 +11,11 @@ Required fields: - `license` (string) - `version` (number) -Hook functions are keyed by the LSM hook name (e.g. `file_open`). +Hook functions are keyed by the supported LSM hook name (e.g. `file_open`). +Use `kernel.lsm_funcs()` to enumerate the supported set. Lua-LSM does not +expose unsupported hooks through its module APIs or observability output; +the current unsupported set includes `getprocattr`, `setprocattr`, and +`lsmprop_to_secctx`. Example: @@ -60,6 +64,8 @@ From userspace (if stats enabled): cat /sys/kernel/security/lua/lsm_funcs ``` +Both interfaces list only hooks that Lua-LSM actually supports and registers. + ## Built-in libraries Lua-LSM preloads these libraries (use `require()`): diff --git a/security/lua/docs/OBSERVABILITY.md b/security/lua/docs/OBSERVABILITY.md index 17efe805d56f..b77d29698de4 100644 --- a/security/lua/docs/OBSERVABILITY.md +++ b/security/lua/docs/OBSERVABILITY.md @@ -28,7 +28,7 @@ cat /sys/kernel/security/lua/lsm_funcs ``` Output columns: -- `name`: LSM hook name +- `name`: supported Lua-LSM hook name - `nlsm`: number of modules implementing the hook - `count`: invocation count - `total`: total time (ns) From 9707c716dfed3548faf185fddd60c244ce714e2f Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Fri, 8 May 2026 20:39:09 +0800 Subject: [PATCH 099/110] lua-lsm: validate inode_init_security xattr output Signed-off-by: Zongyao Chen --- security/lua/lsm_defs.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/security/lua/lsm_defs.c b/security/lua/lsm_defs.c index 4326c13dec3d..8738df9b1344 100644 --- a/security/lua/lsm_defs.c +++ b/security/lua/lsm_defs.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include /* for __MAP */ @@ -769,6 +770,19 @@ LUA_LSM_VOID_DEFINE1(inode_free_security_rcu, void *, inode_security) lua_pushnil(L); /* TODO: inode_security */ } +static int lua_lsm_check_xattr(const char *name, size_t name_len, + size_t value_len) +{ + if (!name_len || name_len > XATTR_NAME_MAX - XATTR_SECURITY_PREFIX_LEN) + return -EINVAL; + if (memchr(name, '\0', name_len)) + return -EINVAL; + if (value_len > XATTR_SIZE_MAX) + return -E2BIG; + + return 0; +} + /** * inode_init_security * Default: -EOPNOTSUPP @@ -822,19 +836,25 @@ LUA_LSM_INT_NAKED_DEFINE5(inode_init_security, struct inode *, inode, ret = -errno; } else if (lua_isstring(L, top) && lua_isstring(L, top + 1)) { struct xattr *xattr; + const char *name; const char *value; - size_t len; + size_t name_len, len; xattr = lsm_get_xattr_slot(xattrs, xattr_count); if (xattr) { + name = lua_tolstring(L, top, &name_len); value = lua_tolstring(L, top + 1, &len); + ret = lua_lsm_check_xattr(name, name_len, len); + if (ret) + break; + xattr->value = kmemdup(value, len, GFP_NOFS); if (xattr->value == NULL) { ret = -ENOMEM; break; } xattr->value_len = len; - xattr->name = lua_tostring(L, top); + xattr->name = name; ret = 0; } else { /* TODO */ From ae861ccfa7cdd5eea34e56db24a32a89a0fdc42a Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Fri, 8 May 2026 20:39:41 +0800 Subject: [PATCH 100/110] lua-lsm: own inode_init_security xattr name storage Signed-off-by: Zongyao Chen --- security/lua/lsm_defs.c | 48 ++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/security/lua/lsm_defs.c b/security/lua/lsm_defs.c index 8738df9b1344..71b3baffa197 100644 --- a/security/lua/lsm_defs.c +++ b/security/lua/lsm_defs.c @@ -770,9 +770,16 @@ LUA_LSM_VOID_DEFINE1(inode_free_security_rcu, void *, inode_security) lua_pushnil(L); /* TODO: inode_security */ } -static int lua_lsm_check_xattr(const char *name, size_t name_len, - size_t value_len) +static int lua_lsm_init_xattr(lua_State *L, int name_idx, int value_idx, + struct xattr *xattr) { + size_t name_len, value_len; + const char *name, *value; + char *buffer; + + name = lua_tolstring(L, name_idx, &name_len); + value = lua_tolstring(L, value_idx, &value_len); + if (!name_len || name_len > XATTR_NAME_MAX - XATTR_SECURITY_PREFIX_LEN) return -EINVAL; if (memchr(name, '\0', name_len)) @@ -780,6 +787,18 @@ static int lua_lsm_check_xattr(const char *name, size_t name_len, if (value_len > XATTR_SIZE_MAX) return -E2BIG; + buffer = kmalloc(value_len + name_len + 1, GFP_NOFS); + if (!buffer) + return -ENOMEM; + + memcpy(buffer, value, value_len); + memcpy(buffer + value_len, name, name_len); + buffer[value_len + name_len] = '\0'; + + /* security_inode_init_security() frees value but not name. */ + xattr->value = buffer; + xattr->value_len = value_len; + xattr->name = buffer + value_len; return 0; } @@ -794,7 +813,7 @@ static int lua_lsm_check_xattr(const char *name, size_t name_len, * false : -EPERM * false, errno : -errno * nil, errno : -errno - * name, value : fill the xattr struct, XXX: name must not be freed + * name, value : fill the xattr struct */ LUA_LSM_INT_NAKED_DEFINE5(inode_init_security, struct inode *, inode, struct inode *, dir, const struct qstr *, qstr, @@ -836,29 +855,10 @@ LUA_LSM_INT_NAKED_DEFINE5(inode_init_security, struct inode *, inode, ret = -errno; } else if (lua_isstring(L, top) && lua_isstring(L, top + 1)) { struct xattr *xattr; - const char *name; - const char *value; - size_t name_len, len; xattr = lsm_get_xattr_slot(xattrs, xattr_count); - if (xattr) { - name = lua_tolstring(L, top, &name_len); - value = lua_tolstring(L, top + 1, &len); - ret = lua_lsm_check_xattr(name, name_len, len); - if (ret) - break; - - xattr->value = kmemdup(value, len, GFP_NOFS); - if (xattr->value == NULL) { - ret = -ENOMEM; - break; - } - xattr->value_len = len; - xattr->name = name; - ret = 0; - } else { - /* TODO */ - } + if (xattr) + ret = lua_lsm_init_xattr(L, top, top + 1, xattr); } break; } From 69ec178b9b153af5ecd544e02d3f3c275b16c2ec Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Fri, 8 May 2026 21:27:17 +0800 Subject: [PATCH 101/110] lua-lsm: document OCFS2 xattr name lifetime issue Signed-off-by: Zongyao Chen --- security/lua/lsm_defs.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/security/lua/lsm_defs.c b/security/lua/lsm_defs.c index 71b3baffa197..17ecd3fe102f 100644 --- a/security/lua/lsm_defs.c +++ b/security/lua/lsm_defs.c @@ -795,7 +795,13 @@ static int lua_lsm_init_xattr(lua_State *L, int name_idx, int value_idx, memcpy(buffer + value_len, name, name_len); buffer[value_len + name_len] = '\0'; - /* security_inode_init_security() frees value but not name. */ + /* + * FIXME: xattr->name must not share xattr->value storage. Some + * initxattrs users, notably OCFS2, duplicate only value and keep + * the name pointer after security_inode_init_security() frees value. + * This needs separate name storage with a lifetime that covers those + * delayed users. + */ xattr->value = buffer; xattr->value_len = value_len; xattr->name = buffer + value_len; From 583c9b96e53af3a023b76f076d2da5fccb3f10c7 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Tue, 12 May 2026 14:57:34 +0800 Subject: [PATCH 102/110] macos: make git config core.symlinks to true --- env_macos.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/env_macos.sh b/env_macos.sh index d628b9c9db7c..3cd1bb8d2261 100755 --- a/env_macos.sh +++ b/env_macos.sh @@ -14,14 +14,12 @@ export PATH="$(brew --prefix lld)/bin:$PATH" export HOSTCFLAGS="-D_UUID_T -D__GETHOSTUUID_H -DO_LARGEFILE=0" -# Use gnu sed instead of builtin sed +# Use gnu coreutils instead of builtin tools alias sed='gsed' # Maybe the symlink is missed on macOS -#ln -s ../../../scripts/syscall.tbl arch/arm64/tools/syscall_64.tbl -#ln -s qcom,sm8550-dispcc.h include/dt-bindings/clock/qcom,sm8650-dispcc.h - +# git config --global core.symlinks true #make ARCH=arm64 LLVM=1 From 0d1bb5098d65e76d397fd1af13184dbed5069010 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Tue, 12 May 2026 16:54:38 +0800 Subject: [PATCH 103/110] lua: rewrite kconfig dependencies Signed-off-by: Tianjia Zhang --- arch/Kconfig | 9 +++++++++ arch/arm64/lib/Makefile | 2 +- arch/x86/lib/Makefile | 2 +- lib/Kconfig | 19 +++++-------------- security/lua/Kconfig | 6 +++--- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/arch/Kconfig b/arch/Kconfig index 31220f512b16..c849bc3db161 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -345,6 +345,15 @@ config ARCH_HAS_SET_MEMORY config ARCH_HAS_SET_DIRECT_MAP bool +# Select if arch has all setjmp/longjmp() functions +config ARCH_HAS_SETJMP + bool + +config ARCH_SETJMP + bool + depends on ARCH_HAS_SETJMP + default n + # # Select if the architecture provides the arch_dma_set_uncached symbol to # either provide an uncached segment alias for a DMA allocation, or diff --git a/arch/arm64/lib/Makefile b/arch/arm64/lib/Makefile index e21e72b2dd77..2236055384cc 100644 --- a/arch/arm64/lib/Makefile +++ b/arch/arm64/lib/Makefile @@ -19,4 +19,4 @@ obj-$(CONFIG_ARM64_MTE) += mte.o obj-$(CONFIG_KASAN_SW_TAGS) += kasan_sw_tags.o -obj-$(CONFIG_SETJMP_ARCH) += setjmp.o +obj-$(CONFIG_ARCH_SETJMP) += setjmp.o diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile index dbb0ce8374bc..e778c71eea99 100644 --- a/arch/x86/lib/Makefile +++ b/arch/x86/lib/Makefile @@ -41,7 +41,7 @@ lib-$(CONFIG_MITIGATION_RETPOLINE) += retpoline.o obj-y += msr.o msr-reg.o msr-reg-export.o hweight.o obj-y += iomem.o -obj-$(CONFIG_SETJMP_ARCH) += setjmp_$(BITS).o +obj-$(CONFIG_ARCH_SETJMP) += setjmp_$(BITS).o ifeq ($(CONFIG_X86_32),y) obj-y += atomic64_32.o diff --git a/lib/Kconfig b/lib/Kconfig index 7885e1f826e3..bc5b972fddca 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -650,22 +650,13 @@ config UNION_FIND config MIN_HEAP bool -config ARCH_HAS_SETJMP - bool - -config SETJMP_ARCH +config HAS_LUA bool depends on ARCH_HAS_SETJMP + default y config LUA - bool "Lua support" - depends on ARCH_HAS_SETJMP + tristate + depends on HAS_LUA + select ARCH_SETJMP default n - select SETJMP_ARCH - help - Build the in-kernel Lua core library for built-in users. - - This is required by subsystems that embed a Lua VM, such as - SECURITY_LUA_LSM. - - If unsure, say N. diff --git a/security/lua/Kconfig b/security/lua/Kconfig index 531ce7d00016..ae64729b388b 100644 --- a/security/lua/Kconfig +++ b/security/lua/Kconfig @@ -2,11 +2,11 @@ config SECURITY_LUA_LSM bool "Lua based LSM Support" - depends on SECURITY - depends on LUA + depends on SECURITY && HAS_LUA + select LUA default n help - This enables the Lua based LSM. + This selects the Lua based LSM. If you are unsure how to answer this question, answer N. config SECURITY_LUA_LSM_STATS From 4eded36918f9314604cd04c80d5b29b18218add6 Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Mon, 11 May 2026 15:32:29 +0800 Subject: [PATCH 104/110] lua-lsm: put looked-up task after descendant walk kernel_task_is_descendant() accepts either a task userdata or a pid. The pid path takes a task reference with find_get_task_by_vpid(), but the RCU walk may replace parent with parent->group_leader before the final put_task_struct(). When the pid names a non-leader thread, this leaks the looked-up task and drops a reference from the group leader without having acquired one here. Keep the looked-up task in a separate pointer and release that reference after the walk. The group leader pointer is only used for comparison under the RCU read-side critical section. Signed-off-by: Zongyao Chen --- security/lua/lua_kernel.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/security/lua/lua_kernel.c b/security/lua/lua_kernel.c index ee3544384682..5ed911301e17 100644 --- a/security/lua/lua_kernel.c +++ b/security/lua/lua_kernel.c @@ -298,14 +298,16 @@ static int kernel_task_is_descendant(lua_State *L) { struct task_struct *child = totask(L, 1); struct task_struct *parent; + struct task_struct *parent_ref = NULL; int tt = lua_type(L, 2); int res = 0; switch (tt) { case LUA_TNUMBER: - parent = find_get_task_by_vpid((pid_t)lua_tointeger(L, 2)); - if (!parent) + parent_ref = find_get_task_by_vpid((pid_t)lua_tointeger(L, 2)); + if (!parent_ref) return luaL_argerror(L, 2, "invalid pid"); + parent = parent_ref; break; case LUA_TUSERDATA: parent = totask(L, 2); @@ -328,8 +330,8 @@ static int kernel_task_is_descendant(lua_State *L) } rcu_read_unlock(); - if (tt == LUA_TNUMBER) - put_task_struct(parent); + if (parent_ref) + put_task_struct(parent_ref); lua_pushboolean(L, res); return 1; From 3dcc78fc40e9eff25e62ad76e4d4790e9faa074e Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Mon, 11 May 2026 17:38:53 +0800 Subject: [PATCH 105/110] lua-lsm: validate numeric capability arguments arg2cap() accepts capability names and numeric capability values. The name path resolves values from a fixed table, but the numeric path returned the Lua integer without checking that it names a valid capability. Callers pass the result to helpers such as cap_raise(), cap_lower(), and cap_capable(). Reject invalid values in arg2cap() so Lua policy cannot drive capability helpers with out-of-range bit numbers. Signed-off-by: Zongyao Chen --- security/lua/auxlib.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/security/lua/auxlib.c b/security/lua/auxlib.c index d1667d5ec26b..5a17abba4984 100644 --- a/security/lua/auxlib.c +++ b/security/lua/auxlib.c @@ -540,6 +540,8 @@ int arg2cap(lua_State *L, int idx) default: return luaL_argerror(L, idx, "integer or string expected"); } + if (!cap_valid(cap)) + return luaL_argerror(L, idx, "invalid capability"); return cap; } From 3ab7fe15071fdb5b0de634518dfcba814f6b3280 Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Mon, 11 May 2026 15:57:37 +0800 Subject: [PATCH 106/110] lua-lsm: return nil for missing wrapped objects Some Lua accessors expose optional kernel pointers. d_backing_inode(), sk->sk_socket, sock->sk, skb->sk, and skb_to_full_sk() can return NULL, but the accessors wrapped those values as typed userdata. Lua policy then sees a non-nil object and can call methods that dereference the stored pointer without another NULL check. Return nil for missing optional objects, matching the existing convention used by Lua-LSM hook argument marshalling. Signed-off-by: Zongyao Chen --- security/lua/lua_fs.c | 3 ++- security/lua/lua_net.c | 16 ++++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/security/lua/lua_fs.c b/security/lua/lua_fs.c index 3f1abdb861f5..77747f6ae00a 100644 --- a/security/lua/lua_fs.c +++ b/security/lua/lua_fs.c @@ -68,8 +68,9 @@ static int fs_dentry_dput(lua_State *L) static int fs_dentry_backing_inode(lua_State *L) { struct dentry *dentry = todentry(L, 1); + struct inode *inode = d_backing_inode(dentry); - *newinode(L) = d_backing_inode(dentry); + inode ? *newinode(L) = inode : lua_pushnil(L); return 1; } diff --git a/security/lua/lua_net.c b/security/lua/lua_net.c index 39ba4d925f7b..ba701c79b019 100644 --- a/security/lua/lua_net.c +++ b/security/lua/lua_net.c @@ -75,7 +75,9 @@ static const char *family_tostring(sa_family_t sa_family) static int net_sock_socket(lua_State *L) { struct sock *sk = tosock(L, 1); - *newsocket(L) = sk->sk_socket; + struct socket *sock = sk->sk_socket; + + sock ? *newsocket(L) = sock : lua_pushnil(L); return 1; } @@ -232,7 +234,9 @@ static int net_socket_release(lua_State *L) static int net_socket_sock(lua_State *L) { struct socket *sock = tosocket(L, 1); - *newsock(L) = sock->sk; + struct sock *sk = sock->sk; + + sk ? *newsock(L) = sk : lua_pushnil(L); return 1; } @@ -255,14 +259,18 @@ static const luaL_Reg socket_meth[] = { static int net_skb_sock(lua_State *L) { struct sk_buff *skb = toskb(L, 1); - *newsock(L) = skb->sk; + struct sock *sk = skb->sk; + + sk ? *newsock(L) = sk : lua_pushnil(L); return 1; } static int net_skb_full_sk(lua_State *L) { struct sk_buff *skb = toskb(L, 1); - *newsock(L) = skb_to_full_sk(skb); + struct sock *sk = skb_to_full_sk(skb); + + sk ? *newsock(L) = sk : lua_pushnil(L); return 1; } From e686d3b938922795e7243670a580a70ec386b603 Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Sat, 9 May 2026 15:16:19 +0800 Subject: [PATCH 107/110] lua-lsm: don't store filp_open errors in file userdata filp_open() can return ERR_PTR(). Keep that value out of the GC-backed userdata so meth_file_gc() never passes an error pointer to fput(). Store the file pointer in a temporary first and publish it to the userdata only after the open succeeds. Signed-off-by: Zongyao Chen --- security/lua/lua_fs.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/security/lua/lua_fs.c b/security/lua/lua_fs.c index 77747f6ae00a..1f992a1c765c 100644 --- a/security/lua/lua_fs.c +++ b/security/lua/lua_fs.c @@ -555,13 +555,15 @@ static int fs_filp_open(lua_State *L) const char *filename = luaL_checkstring(L, 1); int flags = luaL_checkinteger(L, 2); umode_t mode = luaL_checkinteger(L, 3); + struct file *file; struct file **filp = newgcfile(L); - *filp = filp_open(filename, flags, mode); - if (IS_ERR(*filp)) { + file = filp_open(filename, flags, mode); + if (IS_ERR(file)) { lua_pushnil(L); - lua_pushinteger(L, PTR_ERR(*filp)); + lua_pushinteger(L, PTR_ERR(file)); return 2; } + *filp = file; return 1; } From 9224ea6d819acd50bddfdbba38dcb2499013d106 Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Fri, 8 May 2026 17:53:29 +0800 Subject: [PATCH 108/110] lua-lsm: fix length handling for Lua string buffers lua_tolstring() returns the byte length through an out parameter. The marshalling helpers passed pointer-sized storage in places that only need the returned length value. Use the local length variable consistently so Lua strings with embedded NUL bytes are marshalled using the length reported by Lua. Signed-off-by: Zongyao Chen --- security/lua/lsm_defs.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/security/lua/lsm_defs.c b/security/lua/lsm_defs.c index 17ecd3fe102f..788d9d280916 100644 --- a/security/lua/lsm_defs.c +++ b/security/lua/lsm_defs.c @@ -1346,7 +1346,7 @@ LUA_LSM_INT_NAKED_DEFINE3(inode_listsecurity, struct inode *, inode, const char *v = lua_tolstring(L, top, &len); ret = (int)len; if (buffer != NULL && len <= buffer_size) - memcpy(buffer, v, buffer_size); + memcpy(buffer, v, len); } break; default: @@ -1821,7 +1821,7 @@ LUA_LSM_INT_DEFINE2(kernel_load_data, enum kernel_load_data_id, id, LUA_LSM_INT_DEFINE4(kernel_post_load_data, char *, buf, loff_t, size, enum kernel_load_data_id, id, char *, description) { - lua_pushstring(L, (const char *)buf); + lua_pushlstring(L, (const char *)buf, (size_t)size); lua_pushinteger(L, (lua_Integer)size); lua_pushnil(L); /* TODO: id */ lua_pushstring(L, (const char *)description); @@ -1847,7 +1847,7 @@ LUA_LSM_INT_DEFINE4(kernel_post_read_file, struct file *, file, char *, buf, loff_t, size, enum kernel_read_file_id, id) { *newfile(L) = file; - lua_pushstring(L, (const char *)buf); + lua_pushlstring(L, (const char *)buf, (size_t)size); lua_pushinteger(L, (lua_Integer)size); lua_pushnil(L); /* TODO: id */ } From 2d8b56b8306e9d85d85365434bc6f57e5c977584 Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Sat, 9 May 2026 11:29:14 +0800 Subject: [PATCH 109/110] lua-lsm: wait for SRCU readers before freeing module Lua-LSM publishes module pointers under modules_ss SRCU. The unregister path removes a module from the list before freeing it, but readers that already entered the SRCU read-side critical section may still hold the old pointer. Wait for an SRCU grace period after unlinking the module and before freeing its storage. Signed-off-by: Zongyao Chen --- security/lua/lsm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/security/lua/lsm.c b/security/lua/lsm.c index 7b34439f0fae..003c0a94b782 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -1267,6 +1267,7 @@ int lua_lsm_module_unregister(const char *name) if (atomic_sub_return(count, &module->nloaded) == 0) { list_del_rcu(&module->list); + synchronize_srcu(&modules_ss); lua_lsm_module_free(module); err = 0; } else { From 40da5d42ca33396c234aad06a9024177ae2eaca0 Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Fri, 8 May 2026 17:53:53 +0800 Subject: [PATCH 110/110] lua-lsm: fix AF_UNIX sockaddr marshalling lengths AF_UNIX socket addresses are not C strings in all cases. Abstract socket names may contain embedded NUL bytes and their usable length comes from the sockaddr length passed by the hook. Carry the sockaddr length together with the pointer exposed to Lua and use that length when formatting AF_UNIX paths. This preserves abstract socket names and avoids reading past the supplied sockaddr. Signed-off-by: Zongyao Chen --- security/lua/lsm_defs.c | 12 +++++- security/lua/lua_net.c | 45 ++++++++++++++++++++-- security/lua/lua_object.h | 80 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 131 insertions(+), 6 deletions(-) diff --git a/security/lua/lsm_defs.c b/security/lua/lsm_defs.c index 788d9d280916..37afc422ccc1 100644 --- a/security/lua/lsm_defs.c +++ b/security/lua/lsm_defs.c @@ -22,6 +22,7 @@ #include /* for __MAP */ #include /* for ktime_get */ #include +#include #include #include #include @@ -2662,8 +2663,17 @@ static int build_sockaddr(lua_State *L, struct sockaddr *address, int addrlen) if (addrlen < SIN6_LEN_RFC2133) return 0; break; + case AF_UNIX: + if (addrlen < offsetof(struct sockaddr_un, sun_path)) + return 0; + break; + } + { + struct lua_sockaddr *p = newsockaddr(L); + + p->addr = address; + p->addrlen = addrlen; } - *newsockaddr(L) = address; return 1; } diff --git a/security/lua/lua_net.c b/security/lua/lua_net.c index ba701c79b019..1f550692d6f0 100644 --- a/security/lua/lua_net.c +++ b/security/lua/lua_net.c @@ -7,6 +7,7 @@ #include "debug.h" #include +#include #include #include #include @@ -70,6 +71,30 @@ static const char *family_tostring(sa_family_t sa_family) return family; } +static size_t sockaddr_un_path_len(const struct sockaddr_un *sunaddr, int addrlen) +{ + int offset = offsetof(struct sockaddr_un, sun_path); + size_t path_len; + + if (addrlen <= offset) + return 0; + + path_len = addrlen - offset; + + /* + * Pathname sockets are C strings; abstract sockets keep their full + * declared length, including embedded NUL bytes. + */ + if (sunaddr->sun_path[0] != '\0') { + const char *nul = memchr(sunaddr->sun_path, '\0', path_len); + + if (nul) + path_len = nul - sunaddr->sun_path; + } + + return path_len; +} + /*********************************** sock ***********************************/ static int net_sock_socket(lua_State *L) @@ -368,11 +393,16 @@ static int sockaddr_addrs(lua_State *L) } lua_pushinteger(L, ntohs(((struct sockaddr_in6 *)sa)->sin6_port)); return 3; - case AF_UNIX: + case AF_UNIX: { + struct sockaddr_un *sunaddr = (struct sockaddr_un *)sa; + int addrlen = tosockaddr_addrlen(L, 1); + size_t path_len = sockaddr_un_path_len(sunaddr, addrlen); + lua_pushstring(L, "unix"); - lua_pushstring(L, ((struct sockaddr_un *)sa)->sun_path); + lua_pushlstring(L, sunaddr->sun_path, path_len); return 2; } + } return 0; } @@ -391,9 +421,16 @@ static int meth_sockaddr_tostring(lua_State *L) l = snprintf(buffer, sizeof(buffer), "sa.inet6: %pISpc", sa); lua_pushlstring(L, buffer, l); break; - case AF_UNIX: - lua_pushfstring(L, "sa.unix: %s", ((struct sockaddr_un *)sa)->sun_path); + case AF_UNIX: { + struct sockaddr_un *sunaddr = (struct sockaddr_un *)sa; + int addrlen = tosockaddr_addrlen(L, 1); + size_t sp_len = sockaddr_un_path_len(sunaddr, addrlen); + + lua_pushliteral(L, "sa.unix: "); + lua_pushlstring(L, sunaddr->sun_path, sp_len); + lua_concat(L, 2); break; + } case AF_NETLINK: lua_pushfstring(L, "sa.netlink: %d", ((struct sockaddr_nl *)sa)->nl_pid); break; diff --git a/security/lua/lua_object.h b/security/lua/lua_object.h index d7588acc1815..94fdaf86ff5a 100644 --- a/security/lua/lua_object.h +++ b/security/lua/lua_object.h @@ -137,7 +137,6 @@ LUA_OBJECT(func, net, tundev, void *, NULL) \ LUA_OBJECT(func, net, socket, struct socket *, NULL) \ LUA_OBJECT(func, net, skb, struct sk_buff *, NULL) \ - LUA_OBJECT(func, net, sockaddr, struct sockaddr *, NULL) \ LUA_OBJECT(func, security, key, struct key *, NULL) \ LUA_OBJECT(object, block, bdev, struct block_device *, NULL) \ LUA_OBJECT(object, fs, inode, struct inode *, NULL) \ @@ -157,4 +156,83 @@ LUA_OBJECTS_LIST #undef LUA_OBJECT +/* + * sockaddr is defined manually because it needs to carry addrlen alongside + * the pointer, so Lua-side methods can derive correct bounds for AF_UNIX + * sun_path (including abstract namespace sockets with embedded NULs). + * + * NOTE: These definitions mirror what LUA_OBJECT_func_DEFINE would generate. + * If that macro is ever extended (e.g. new raw/gc helpers), update this + * section to stay in sync. + */ +struct lua_sockaddr { + struct sockaddr *addr; + int addrlen; +}; + +static inline struct lua_sockaddr *newsockaddr_nomain(lua_State *L) +{ + struct lua_sockaddr *p = lua_newuserdata(L, sizeof(*p)); + + p->addr = NULL; + p->addrlen = 0; + luaL_getmetatable(L, METHOD_NAME(sockaddr)); + lua_setmetatable(L, -2); + return p; +} + +static inline struct lua_sockaddr *newsockaddr(lua_State *L) +{ + struct lua_sockaddr *p = newsockaddr_nomain(L); + + lua_getfield(L, LUA_REGISTRYINDEX, CURR_ENV); + lua_setfenv(L, -2); + return p; +} + +static inline struct lua_sockaddr *tosockaddarp(lua_State *L, int idx) +{ + return (struct lua_sockaddr *)checkudata3(L, idx, METHOD_NAME(sockaddr)); +} + +static inline struct sockaddr *tosockaddr(lua_State *L, int idx) +{ + return tosockaddarp(L, idx)->addr; +} + +static inline int tosockaddr_addrlen(lua_State *L, int idx) +{ + return tosockaddarp(L, idx)->addrlen; +} + +static int rawmeth_sockaddr_type(lua_State *L) +{ + lua_pushstring(L, "sockaddr"); + lua_pushboolean(L, 0); + return 2; +} + +static int rawmeth_sockaddr_tostring(lua_State *L) +{ + struct sockaddr *o = tosockaddr(L, 1); + + lua_pushfstring(L, "sockaddr: <%p>", o); + return 1; +} + +static inline void create_sockaddr_meta(lua_State *L, + const luaL_Reg *funcs, const luaL_Reg *gc) +{ + static const luaL_Reg basemeths[] = { + { "__tostring", rawmeth_sockaddr_tostring }, + { NULL, NULL } + }; + static const luaL_Reg rawmeths[] = { + { "type", rawmeth_sockaddr_type }, + { NULL, NULL } + }; + createmeta3(L, "sockaddr", basemeths, METHOD_NAME_GC(sockaddr), gc, + METHOD_NAME(sockaddr), funcs, METHOD_NAME_RAW(sockaddr), rawmeths); +} + #endif /* ! _SECURITY_LUA_LSM_LUA_OBJECT_H */