From 094f33a8c28a284b963dde241b78a6872df9d717 Mon Sep 17 00:00:00 2001 From: Samu Voutilainen Date: Fri, 24 Mar 2023 06:48:31 +0200 Subject: [PATCH 01/49] Correct declaration for query_blocked(). Fixes build on OpenSUSE Tumbleweed. Signed-off-by: Samu Voutilainen --- src/dnsmasq_interface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dnsmasq_interface.c b/src/dnsmasq_interface.c index 608801b8a..aae62d2a5 100644 --- a/src/dnsmasq_interface.c +++ b/src/dnsmasq_interface.c @@ -62,7 +62,7 @@ static void _query_set_reply(const unsigned int flags, const enum reply_type rep static bool _FTL_check_blocking(int queryID, int domainID, int clientID, const char* file, const int line); static unsigned long converttimeval(const struct timeval time) __attribute__((const)); static enum query_status detect_blocked_IP(const unsigned short flags, const union all_addr *addr, const queriesData *query, const domainsData *domain); -static void query_blocked(queriesData* query, domainsData* domain, clientsData* client, const unsigned char new_status); +static void query_blocked(queriesData* query, domainsData* domain, clientsData* client, const enum query_status new_status); static void FTL_forwarded(const unsigned int flags, const char *name, const union all_addr *addr, unsigned short port, const int id, const char* file, const int line); static void FTL_reply(const unsigned int flags, const char *name, const union all_addr *addr, const char* arg, const int id, const char* file, const int line); static void FTL_upstream_error(const union all_addr *addr, const unsigned int flags, const int id, const char* file, const int line); From 2f29edb8be4850beb339effea45b0fa91ab1cd92 Mon Sep 17 00:00:00 2001 From: Samu Voutilainen Date: Fri, 24 Mar 2023 06:52:21 +0200 Subject: [PATCH 02/49] Correct declaration for blockingstatus variable. Signed-off-by: Samu Voutilainen --- src/setupVars.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/setupVars.h b/src/setupVars.h index 7588be649..8a1c346b1 100644 --- a/src/setupVars.h +++ b/src/setupVars.h @@ -20,6 +20,6 @@ char* find_equals(const char* s) __attribute__((pure)); void trim_whitespace(char *string); void check_blocking_status(void); -extern unsigned char blockingstatus; +extern enum blocking_status blockingstatus; #endif //SETUPVARS_H From 28ec1f04c45e0f254d701f3826787c584a4090d6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 25 Mar 2023 10:56:46 +0000 Subject: [PATCH 03/49] Bump actions/stale from 7.0.0 to 8.0.0 Bumps [actions/stale](https://github.com/actions/stale) from 7.0.0 to 8.0.0. - [Release notes](https://github.com/actions/stale/releases) - [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/stale/compare/v7.0.0...v8.0.0) --- updated-dependencies: - dependency-name: actions/stale dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/stale.yml | 2 +- .github/workflows/stale_pr.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 9c3c9829b..e183ce17a 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -13,7 +13,7 @@ jobs: issues: write steps: - - uses: actions/stale@v7.0.0 + - uses: actions/stale@v8.0.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} days-before-stale: 30 diff --git a/.github/workflows/stale_pr.yml b/.github/workflows/stale_pr.yml index 7c753d7da..e9230b2c7 100644 --- a/.github/workflows/stale_pr.yml +++ b/.github/workflows/stale_pr.yml @@ -17,7 +17,7 @@ jobs: pull-requests: write steps: - - uses: actions/stale@v7.0.0 + - uses: actions/stale@v8.0.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} # Do not automatically mark PR/issue as stale From 5ad479b6289ff715f0ce4fa3ce16824e55d0f126 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 25 Mar 2023 10:56:49 +0000 Subject: [PATCH 04/49] Bump actions/checkout from 3.4.0 to 3.5.0 Bumps [actions/checkout](https://github.com/actions/checkout) from 3.4.0 to 3.5.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3.4.0...v3.5.0) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 6 +++--- .github/workflows/codespell.yml | 2 +- .github/workflows/sync-back-to-dev.yml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 126208384..4815fc1db 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3.4.0 + uses: actions/checkout@v3.5.0 - name: "Calculate required variables" id: variables @@ -90,7 +90,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3.4.0 + uses: actions/checkout@v3.5.0 - name: "Fix ownership of repository" run: chown -R root . @@ -133,7 +133,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3.4.0 + uses: actions/checkout@v3.5.0 - name: Get Binaries built in previous jobs uses: actions/download-artifact@v3.0.2 diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index 6ba65017a..a04908f3a 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -10,7 +10,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3.4.0 + uses: actions/checkout@v3.5.0 - name: Spell-Checking uses: codespell-project/actions-codespell@master diff --git a/.github/workflows/sync-back-to-dev.yml b/.github/workflows/sync-back-to-dev.yml index b8ecdb715..79111dddb 100644 --- a/.github/workflows/sync-back-to-dev.yml +++ b/.github/workflows/sync-back-to-dev.yml @@ -11,7 +11,7 @@ jobs: name: Syncing branches steps: - name: Checkout - uses: actions/checkout@v3.4.0 + uses: actions/checkout@v3.5.0 - name: Opening pull request run: gh pr create -B development -H master --title 'Sync master back into development' --body 'Created by Github action' --label 'internal' env: From 37de8662eafcfa5e83d9b1e75274b7e072afae0d Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 7 Apr 2023 17:51:38 +0200 Subject: [PATCH 05/49] Simplify EDNS handling code and also interpret replies received from upstream Signed-off-by: DL6ER --- src/dnsmasq/forward.c | 18 +++++------ src/dnsmasq_interface.c | 9 +++--- src/dnsmasq_interface.h | 4 +-- src/edns0.c | 67 ++++++++++++++++++++++++++--------------- src/edns0.h | 9 ++++-- 5 files changed, 63 insertions(+), 44 deletions(-) diff --git a/src/dnsmasq/forward.c b/src/dnsmasq/forward.c index c10700676..50fe47a9a 100644 --- a/src/dnsmasq/forward.c +++ b/src/dnsmasq/forward.c @@ -783,7 +783,8 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server a.log.rcode = rcode; a.log.ede = ede; log_query(F_UPSTREAM | F_RCODE, "error", &a, NULL, 0); - + FTL_parse_pseudoheaders(pheader, (size_t)plen); + return resize_packet(header, n, pheader, plen); } @@ -1750,9 +1751,8 @@ void receive_query(struct listener *listen, time_t now) have_mark = get_incoming_mark(&source_addr, &dst_addr, /* istcp: */ 0, &mark); #endif //********************** Pi-hole modification **********************// - ednsData edns = { 0 }; - if (find_pseudoheader(header, (size_t)n, NULL, &pheader, NULL, NULL)) - FTL_parse_pseudoheaders(header, n, &source_addr, &edns); + if ((pheader = find_pseudoheader(header, (size_t)n, NULL, &pheader, NULL, NULL))) + FTL_parse_pseudoheaders(pheader, (size_t)n); //******************************************************************// if (extract_request(header, (size_t)n, daemon->namebuff, &type)) @@ -1763,7 +1763,7 @@ void receive_query(struct listener *listen, time_t now) log_query_mysockaddr(F_QUERY | F_FORWARD, daemon->namebuff, &source_addr, auth_dns ? "auth" : "query", type); piholeblocked = FTL_new_query(F_QUERY | F_FORWARD , daemon->namebuff, - &source_addr, auth_dns ? "auth" : "query", type, daemon->log_display_id, &edns, UDP); + &source_addr, auth_dns ? "auth" : "query", type, daemon->log_display_id, UDP); #ifdef HAVE_CONNTRACK is_single_query = 1; @@ -2298,9 +2298,9 @@ unsigned char *tcp_request(int confd, time_t now, no_cache_dnssec = 1; //********************** Pi-hole modification **********************// - ednsData edns = { 0 }; - if (find_pseudoheader(header, (size_t)size, NULL, &pheader, NULL, NULL)) - FTL_parse_pseudoheaders(header, size, &peer_addr, &edns); + unsigned char *pheader = NULL; + if ((pheader = find_pseudoheader(header, (size_t)size, NULL, &pheader, NULL, NULL))) + FTL_parse_pseudoheaders(pheader, (size_t)size); //******************************************************************// if ((gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype))) @@ -2320,7 +2320,7 @@ unsigned char *tcp_request(int confd, time_t now, &peer_addr, auth_dns ? "auth" : "query", qtype); piholeblocked = FTL_new_query(F_QUERY | F_FORWARD, daemon->namebuff, - &peer_addr, auth_dns ? "auth" : "query", qtype, daemon->log_display_id, &edns, TCP); + &peer_addr, auth_dns ? "auth" : "query", qtype, daemon->log_display_id, TCP); #ifdef HAVE_AUTH /* find queries for zones we're authoritative for, and answer them directly */ diff --git a/src/dnsmasq_interface.c b/src/dnsmasq_interface.c index aae62d2a5..61bb065b3 100644 --- a/src/dnsmasq_interface.c +++ b/src/dnsmasq_interface.c @@ -128,8 +128,6 @@ void FTL_hook(unsigned int flags, const char *name, union all_addr *addr, char * if(!config.show_dnssec) return; - const ednsData edns = { 0 }; - // Type is overloaded with port since 2d65d55, so we have to // derive the real query type from the arg string unsigned short qtype = type; @@ -158,7 +156,7 @@ void FTL_hook(unsigned int flags, const char *name, union all_addr *addr, char * arg = (char*)"dnssec-unknown"; } - _FTL_new_query(flags, name, NULL, arg, qtype, id, &edns, INTERNAL, file, line); + _FTL_new_query(flags, name, NULL, arg, qtype, id, INTERNAL, file, line); // forwarded upstream (type is used to store the upstream port) FTL_forwarded(flags, name, addr, type, id, path, line); } @@ -464,7 +462,7 @@ static bool is_pihole_domain(const char *domain) bool _FTL_new_query(const unsigned int flags, const char *name, union mysockaddr *addr, char *arg, const unsigned short qtype, const int id, - const ednsData *edns, const enum protocol proto, + const enum protocol proto, const char* file, const int line) { // Create new query in data structure @@ -596,6 +594,7 @@ bool _FTL_new_query(const unsigned int flags, const char *name, in_port_t clientPort = daemon->port; bool internal_query = false; char clientIP[ADDRSTRLEN+1] = { 0 }; + ednsData *edns = getEDNS(); if(config.edns0_ecs && edns && edns->client_set) { // Use ECS provided client @@ -3345,7 +3344,7 @@ int check_struct_sizes(void) result += check_one_struct("clientsData", sizeof(clientsData), 672, 648); result += check_one_struct("domainsData", sizeof(domainsData), 24, 20); result += check_one_struct("DNSCacheData", sizeof(DNSCacheData), 16, 16); - result += check_one_struct("ednsData", sizeof(ednsData), 72, 72); + result += check_one_struct("ednsData", sizeof(ednsData), 76, 76); result += check_one_struct("overTimeData", sizeof(overTimeData), 32, 24); result += check_one_struct("regexData", sizeof(regexData), 64, 48); result += check_one_struct("SharedMemory", sizeof(SharedMemory), 24, 12); diff --git a/src/dnsmasq_interface.h b/src/dnsmasq_interface.h index fdd5ed698..e0d11cb5b 100644 --- a/src/dnsmasq_interface.h +++ b/src/dnsmasq_interface.h @@ -23,8 +23,8 @@ void FTL_hook(unsigned int flags, const char *name, union all_addr *addr, char * #define FTL_iface(iface, addr, addrfamily) _FTL_iface(iface, addr, addrfamily, __FILE__, __LINE__) void _FTL_iface(struct irec *recviface, const union all_addr *addr, const sa_family_t addrfamily, const char* file, const int line); -#define FTL_new_query(flags, name, addr, arg, qtype, id, edns, proto) _FTL_new_query(flags, name, addr, arg, qtype, id, edns, proto, __FILE__, __LINE__) -bool _FTL_new_query(const unsigned int flags, const char *name, union mysockaddr *addr, char *arg, const unsigned short qtype, const int id, const ednsData *edns, enum protocol proto, const char* file, const int line); +#define FTL_new_query(flags, name, addr, arg, qtype, id, proto) _FTL_new_query(flags, name, addr, arg, qtype, id, proto, __FILE__, __LINE__) +bool _FTL_new_query(const unsigned int flags, const char *name, union mysockaddr *addr, char *arg, const unsigned short qtype, const int id, enum protocol proto, const char* file, const int line); #define FTL_header_analysis(header4, rcode, server, id) _FTL_header_analysis(header4, rcode, server, id, __FILE__, __LINE__) void _FTL_header_analysis(const unsigned char header4, const unsigned int rcode, const struct server *server, const int id, const char* file, const int line); diff --git a/src/edns0.c b/src/edns0.c index 35860381d..1352d0029 100644 --- a/src/edns0.c +++ b/src/edns0.c @@ -41,14 +41,26 @@ // dnsmasq option: --add-cpe-id=... #define EDNS0_CPE_ID EDNS0_OPTION_NOMCPEID -void FTL_parse_pseudoheaders(struct dns_header *header, size_t n, union mysockaddr *peer, ednsData *edns) +static ednsData edns = { 0 }; + +ednsData *getEDNS(void) { - int is_sign; - size_t plen; - unsigned char *pheader, *sizep; + if(edns.valid) + { + // Return pointer to ednsData structure and reset it for the + // next query + edns.valid = false; + return &edns; + } - // Extract additional record A.K.A. pseudoheader - if (!(pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign, NULL))) + // No valid EDNS data available + return NULL; +} + +void FTL_parse_pseudoheaders(unsigned char *pheader, const size_t plen) +{ + // Return early if we have no pseudoheader (a.k.a. additional records) + if (!pheader) return; // Debug logging @@ -152,6 +164,11 @@ void FTL_parse_pseudoheaders(struct dns_header *header, size_t n, union mysockad if(edns0_version != 0x00) return; + // Reset EDNS(0) data + memset(&edns, 0, sizeof(ednsData)); + edns.ede = -1; + edns.valid = true; + size_t offset; // The header is 11 bytes before the beginning of OPTION-DATA while ((offset = (p - pheader - 11u)) < rdlen && rdlen < UINT16_MAX) { @@ -225,8 +242,8 @@ void FTL_parse_pseudoheaders(struct dns_header *header, size_t n, union mysockad } // Copy data to edns struct - strncpy(edns->client, ipaddr, ADDRSTRLEN); - edns->client[ADDRSTRLEN-1] = '\0'; + strncpy(edns.client, ipaddr, ADDRSTRLEN); + edns.client[ADDRSTRLEN-1] = '\0'; // Only set the address as useful when it is not the // loopback address of the distant machine (127.0.0.0/8 or ::1) @@ -239,7 +256,7 @@ void FTL_parse_pseudoheaders(struct dns_header *header, size_t n, union mysockad } else { - edns->client_set = true; + edns.client_set = true; if(config.debug & DEBUG_EDNS0) logg("EDNS(0) CLIENT SUBNET: %s/%u - OK (IPv%u)", ipaddr, source_netmask, family == 1 ? 4 : 6); @@ -292,11 +309,11 @@ void FTL_parse_pseudoheaders(struct dns_header *header, size_t n, union mysockad else if(code == EDNS0_MAC_ADDR_BYTE && optlen == 6) { // EDNS(0) MAC address (BYTE format) - memcpy(edns->mac_byte, p, sizeof(edns->mac_byte)); - print_mac(edns->mac_text, (unsigned char*)edns->mac_byte, sizeof(edns->mac_byte)); - edns->mac_set = true; + memcpy(edns.mac_byte, p, sizeof(edns.mac_byte)); + print_mac(edns.mac_text, (unsigned char*)edns.mac_byte, sizeof(edns.mac_byte)); + edns.mac_set = true; if(config.debug & DEBUG_EDNS0) - logg("EDNS(0) MAC address (BYTE format): %s", edns->mac_text); + logg("EDNS(0) MAC address (BYTE format): %s", edns.mac_text); // Advance working pointer p += 6; @@ -304,19 +321,19 @@ void FTL_parse_pseudoheaders(struct dns_header *header, size_t n, union mysockad else if(code == EDNS0_MAC_ADDR_TEXT && optlen == 17) { // EDNS(0) MAC address (TEXT format) - memcpy(edns->mac_text, p, 17); - edns->mac_text[17] = '\0'; - if(sscanf(edns->mac_text, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", - &edns->mac_byte[0], - &edns->mac_byte[1], - &edns->mac_byte[2], - &edns->mac_byte[3], - &edns->mac_byte[4], - &edns->mac_byte[5]) == 6) + memcpy(edns.mac_text, p, 17); + edns.mac_text[17] = '\0'; + if(sscanf(edns.mac_text, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + (unsigned char*)&edns.mac_byte[0], + (unsigned char*)&edns.mac_byte[1], + (unsigned char*)&edns.mac_byte[2], + (unsigned char*)&edns.mac_byte[3], + (unsigned char*)&edns.mac_byte[4], + (unsigned char*)&edns.mac_byte[5]) == 6) { - edns->mac_set = true; + edns.mac_set = true; if(config.debug & DEBUG_EDNS0) - logg("EDNS(0) MAC address (TEXT format): %s", edns->mac_text); + logg("EDNS(0) MAC address (TEXT format): %s", edns.mac_text); } else if(config.debug & DEBUG_EDNS0) { @@ -358,7 +375,7 @@ void FTL_parse_pseudoheaders(struct dns_header *header, size_t n, union mysockad else { if(config.debug & DEBUG_EDNS0) - logg("EDNS(0):n option %u with length %u", code, optlen); + logg("EDNS(0): option %u with length %u", code, optlen); // Not implemented, skip this record // Advance working pointer diff --git a/src/edns0.h b/src/edns0.h index 1ab2c9b42..9fd3b592e 100644 --- a/src/edns0.h +++ b/src/edns0.h @@ -11,13 +11,16 @@ #define EDNS0_HEADER typedef struct { - bool client_set; - bool mac_set; + bool client_set :1; + bool mac_set :1; + bool valid :1; char client[ADDRSTRLEN]; char mac_byte[6]; char mac_text[18]; + int ede; } ednsData; -void FTL_parse_pseudoheaders(struct dns_header *header, size_t n, union mysockaddr *peer, ednsData *edns); +ednsData *getEDNS(void); +void FTL_parse_pseudoheaders(unsigned char *pheader, const size_t plen); #endif // EDNS0_HEADER From f99bda18956a7455183d07aba2bae2309932e0d8 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 7 Apr 2023 18:01:28 +0200 Subject: [PATCH 06/49] Implement EDNS(0) EDE Signed-off-by: DL6ER --- src/edns0.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/edns0.c b/src/edns0.c index 1352d0029..d7797b600 100644 --- a/src/edns0.c +++ b/src/edns0.c @@ -372,6 +372,21 @@ void FTL_parse_pseudoheaders(unsigned char *pheader, const size_t plen) // Advance working pointer p += optlen; } + else if(code == EDNS0_OPTION_EDE && optlen == 2) + { + // EDNS(0) EDE + // https://datatracker.ietf.org/doc/rfc8914/ + + // The INFO-CODE from the EDE EDNS option is used to + // serve as an index into the "Extended DNS Error" IANA + // registry, the initial values for which are defined in + edns.ede = ntohs(((int)p[1] << 8) | p[0]); + if(config.debug & DEBUG_EDNS0) + logg("EDNS(0) EDE: %s (code %d)", edestr(edns.ede), edns.ede); + + // Advance working pointer + p += optlen; + } else { if(config.debug & DEBUG_EDNS0) From 3ac34d323bbdbe99ef605a05ca44c88efa0c8bef Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 7 Apr 2023 18:52:15 +0200 Subject: [PATCH 07/49] Use AD bit for IN/SECURE and EDE in SERVFAIL when prox for BOGUSy-dnsmasq option is used Signed-off-by: DL6ER --- src/dnsmasq/forward.c | 2 +- src/dnsmasq_interface.c | 36 ++++++++++++++++++++++++++++++++++-- src/edns0.c | 7 +++++-- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/dnsmasq/forward.c b/src/dnsmasq/forward.c index 50fe47a9a..36bf9ee97 100644 --- a/src/dnsmasq/forward.c +++ b/src/dnsmasq/forward.c @@ -782,8 +782,8 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server union all_addr a; a.log.rcode = rcode; a.log.ede = ede; - log_query(F_UPSTREAM | F_RCODE, "error", &a, NULL, 0); FTL_parse_pseudoheaders(pheader, (size_t)plen); + log_query(F_UPSTREAM | F_RCODE, "error", &a, NULL, 0); return resize_packet(header, n, pheader, plen); } diff --git a/src/dnsmasq_interface.c b/src/dnsmasq_interface.c index 61bb065b3..9c70e5055 100644 --- a/src/dnsmasq_interface.c +++ b/src/dnsmasq_interface.c @@ -76,6 +76,7 @@ static char *get_ptrname(struct in_addr *addr); static const char *check_dnsmasq_name(const char *name); // Static blocking metadata +static bool adbit = false; static const char *blockingreason = ""; static enum reply_type force_next_DNS_reply = REPLY_UNKNOWN; static int last_regex_idx = -1; @@ -2016,6 +2017,12 @@ static void FTL_reply(const unsigned int flags, const char *name, const union al if(config.debug & DEBUG_QUERIES) logg(" EDE: %s (%d)", edestr(addr->log.ede), addr->log.ede); } + ednsData *edns = getEDNS(); + if(edns != NULL && edns->ede != EDE_UNSET) + { + query->ede = edns->ede; + log_debug(DEBUG_QUERIES, " EDE: %s (%d)", edestr(edns->ede), edns->ede); + } // Update upstream server (if applicable) if(!cached) @@ -2182,6 +2189,13 @@ static void FTL_reply(const unsigned int flags, const char *name, const union al logg("***** Unknown upstream REPLY"); } + if(query && option_bool(OPT_DNSSEC_PROXY)) + { + // DNSSEC proxy mode is enabled. Interpret AD flag + // and set DNSSEC status accordingly + query_set_dnssec(query, adbit ? DNSSEC_SECURE : DNSSEC_INSECURE); + } + unlock_shm(); } @@ -2438,6 +2452,9 @@ static void FTL_upstream_error(const union all_addr *addr, const unsigned int fl break; } + // Get EDNS data (if available) + ednsData *edns = getEDNS(); + // Debug logging if(config.debug & DEBUG_QUERIES) { @@ -2480,8 +2497,20 @@ static void FTL_upstream_error(const union all_addr *addr, const unsigned int fl if(addr->log.ede != EDE_UNSET) // This function is only called if (flags & F_RCODE) logg(" EDE: %s (%d)", edestr(addr->log.ede), addr->log.ede); - } + if(edns != NULL && edns->ede != EDE_UNSET) + { + query->ede = edns->ede; + log_debug(DEBUG_QUERIES, " EDE: %s (%d)", edestr(edns->ede), edns->ede); + } + } + if(option_bool(OPT_DNSSEC_PROXY) && edns->ede >= EDE_DNSSEC_BOGUS && edns->ede <= EDE_NO_NSEC) + { + // DNSSEC proxy mode is enabled and we received a DNSSEC status + // from the upstream server. We need to update the DNSSEC status + // of the corresponding query. + query_set_dnssec(query, DNSSEC_BOGUS); + } // Set query reply query_set_reply(0, reply, addr, query, response); @@ -2561,6 +2590,9 @@ void _FTL_header_analysis(const unsigned char header4, const unsigned int rcode, // RA bit is not set and rcode is NXDOMAIN FTL_mark_externally_blocked(id, file, line); + // Check if AD bit is set in DNS header + adbit = header4 & HB4_AD; + // Store server which sent this reply if(server) { @@ -3300,7 +3332,7 @@ const char *get_edestr(const int ede) static void _query_set_dnssec(queriesData *query, const enum dnssec_status dnssec, const char *file, const int line) { // Return early if DNSSEC validation is disabled - if(!option_bool(OPT_DNSSEC_VALID)) + if(!option_bool(OPT_DNSSEC_VALID) && !option_bool(OPT_DNSSEC_PROXY)) return; if(config.debug & DEBUG_DNSSEC) diff --git a/src/edns0.c b/src/edns0.c index d7797b600..e7a20153b 100644 --- a/src/edns0.c +++ b/src/edns0.c @@ -166,7 +166,7 @@ void FTL_parse_pseudoheaders(unsigned char *pheader, const size_t plen) // Reset EDNS(0) data memset(&edns, 0, sizeof(ednsData)); - edns.ede = -1; + edns.ede = EDE_UNSET; edns.valid = true; size_t offset; // The header is 11 bytes before the beginning of OPTION-DATA @@ -379,7 +379,10 @@ void FTL_parse_pseudoheaders(unsigned char *pheader, const size_t plen) // The INFO-CODE from the EDE EDNS option is used to // serve as an index into the "Extended DNS Error" IANA - // registry, the initial values for which are defined in + // registry, the initial values for which are defined in + // this document. The value of the INFO-CODE is encoded + // as a two-octet unsigned integer in network byte + // order. edns.ede = ntohs(((int)p[1] << 8) | p[0]); if(config.debug & DEBUG_EDNS0) logg("EDNS(0) EDE: %s (code %d)", edestr(edns.ede), edns.ede); From 8a4488ceb0f6c3c1d967843c670b24cc4b02a84a Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 7 Apr 2023 18:56:36 +0200 Subject: [PATCH 08/49] Ignore possible EXTRA-TEXT field in EDNS0 EDE data Signed-off-by: DL6ER --- src/dnsmasq_interface.c | 5 +++-- src/edns0.c | 23 ++++++++++++++++++++--- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/dnsmasq_interface.c b/src/dnsmasq_interface.c index 9c70e5055..06a239ffa 100644 --- a/src/dnsmasq_interface.c +++ b/src/dnsmasq_interface.c @@ -2021,7 +2021,8 @@ static void FTL_reply(const unsigned int flags, const char *name, const union al if(edns != NULL && edns->ede != EDE_UNSET) { query->ede = edns->ede; - log_debug(DEBUG_QUERIES, " EDE: %s (%d)", edestr(edns->ede), edns->ede); + if(config.debug & DEBUG_QUERIES) + logg(" EDE: %s (%d)", edestr(edns->ede), edns->ede); } // Update upstream server (if applicable) @@ -2501,7 +2502,7 @@ static void FTL_upstream_error(const union all_addr *addr, const unsigned int fl if(edns != NULL && edns->ede != EDE_UNSET) { query->ede = edns->ede; - log_debug(DEBUG_QUERIES, " EDE: %s (%d)", edestr(edns->ede), edns->ede); + logg(" EDE: %s (%d)", edestr(edns->ede), edns->ede); } } if(option_bool(OPT_DNSSEC_PROXY) && edns->ede >= EDE_DNSSEC_BOGUS && edns->ede <= EDE_NO_NSEC) diff --git a/src/edns0.c b/src/edns0.c index e7a20153b..3bb524bed 100644 --- a/src/edns0.c +++ b/src/edns0.c @@ -372,18 +372,35 @@ void FTL_parse_pseudoheaders(unsigned char *pheader, const size_t plen) // Advance working pointer p += optlen; } - else if(code == EDNS0_OPTION_EDE && optlen == 2) + else if(code == EDNS0_OPTION_EDE && optlen >= 2) { // EDNS(0) EDE // https://datatracker.ietf.org/doc/rfc8914/ - + // + // 1 1 1 1 1 1 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + // 0: | OPTION-CODE | + // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + // 2: | OPTION-LENGTH | + // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + // 4: | INFO-CODE | + edns.ede = ntohs(((int)p[1] << 8) | p[0]); + // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + // 6: / EXTRA-TEXT ... / + // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + // // The INFO-CODE from the EDE EDNS option is used to // serve as an index into the "Extended DNS Error" IANA // registry, the initial values for which are defined in // this document. The value of the INFO-CODE is encoded // as a two-octet unsigned integer in network byte // order. - edns.ede = ntohs(((int)p[1] << 8) | p[0]); + // + // The EXTRA-TEXT from the EDE EDNS option is ignored by + // FTL + + // Debug output if(config.debug & DEBUG_EDNS0) logg("EDNS(0) EDE: %s (code %d)", edestr(edns.ede), edns.ede); From ad85e0f3f66447f73afd6afa6271d67ff543a685 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 8 Apr 2023 12:54:54 +0200 Subject: [PATCH 09/49] Only try to interpret EDNS EDE when EDE data is available Signed-off-by: DL6ER --- src/dnsmasq_interface.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/dnsmasq_interface.c b/src/dnsmasq_interface.c index 06a239ffa..b2bcf8943 100644 --- a/src/dnsmasq_interface.c +++ b/src/dnsmasq_interface.c @@ -2505,11 +2505,13 @@ static void FTL_upstream_error(const union all_addr *addr, const unsigned int fl logg(" EDE: %s (%d)", edestr(edns->ede), edns->ede); } } - if(option_bool(OPT_DNSSEC_PROXY) && edns->ede >= EDE_DNSSEC_BOGUS && edns->ede <= EDE_NO_NSEC) + // Check EDNS EDE for DNSSEC status in DNSSEC proxy mode + if(option_bool(OPT_DNSSEC_PROXY) && + edns && edns->ede >= EDE_DNSSEC_BOGUS && edns->ede <= EDE_NO_NSEC) { - // DNSSEC proxy mode is enabled and we received a DNSSEC status - // from the upstream server. We need to update the DNSSEC status - // of the corresponding query. + // DNSSEC proxy mode is enabled and we received a valid DNSSEC + // status from the upstream server through ENDS EDE. We need to + // update the DNSSEC status of the corresponding query. query_set_dnssec(query, DNSSEC_BOGUS); } // Set query reply From 538c6a058ceb35f25a07381196e28d721c7615fc Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 8 Apr 2023 14:30:40 +0200 Subject: [PATCH 10/49] Log if EDNS header is NULL and we are in debug mode Signed-off-by: DL6ER --- src/dnsmasq/forward.c | 8 ++++---- src/dnsmasq_interface.c | 3 +++ src/edns0.c | 4 ++++ 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/dnsmasq/forward.c b/src/dnsmasq/forward.c index 36bf9ee97..0725e216d 100644 --- a/src/dnsmasq/forward.c +++ b/src/dnsmasq/forward.c @@ -1751,8 +1751,8 @@ void receive_query(struct listener *listen, time_t now) have_mark = get_incoming_mark(&source_addr, &dst_addr, /* istcp: */ 0, &mark); #endif //********************** Pi-hole modification **********************// - if ((pheader = find_pseudoheader(header, (size_t)n, NULL, &pheader, NULL, NULL))) - FTL_parse_pseudoheaders(pheader, (size_t)n); + pheader = find_pseudoheader(header, (size_t)n, NULL, &pheader, NULL, NULL); + FTL_parse_pseudoheaders(pheader, (size_t)n); //******************************************************************// if (extract_request(header, (size_t)n, daemon->namebuff, &type)) @@ -2299,8 +2299,8 @@ unsigned char *tcp_request(int confd, time_t now, //********************** Pi-hole modification **********************// unsigned char *pheader = NULL; - if ((pheader = find_pseudoheader(header, (size_t)size, NULL, &pheader, NULL, NULL))) - FTL_parse_pseudoheaders(pheader, (size_t)size); + pheader = find_pseudoheader(header, (size_t)size, NULL, &pheader, NULL, NULL); + FTL_parse_pseudoheaders(pheader, (size_t)size); //******************************************************************// if ((gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype))) diff --git a/src/dnsmasq_interface.c b/src/dnsmasq_interface.c index b2bcf8943..65af3796e 100644 --- a/src/dnsmasq_interface.c +++ b/src/dnsmasq_interface.c @@ -2497,7 +2497,10 @@ static void FTL_upstream_error(const union all_addr *addr, const unsigned int fl } if(addr->log.ede != EDE_UNSET) // This function is only called if (flags & F_RCODE) + { + query->ede = addr->log.ede; logg(" EDE: %s (%d)", edestr(addr->log.ede), addr->log.ede); + } if(edns != NULL && edns->ede != EDE_UNSET) { diff --git a/src/edns0.c b/src/edns0.c index 3bb524bed..644492fab 100644 --- a/src/edns0.c +++ b/src/edns0.c @@ -61,7 +61,11 @@ void FTL_parse_pseudoheaders(unsigned char *pheader, const size_t plen) { // Return early if we have no pseudoheader (a.k.a. additional records) if (!pheader) + { + if(config.debug & DEBUG_EDNS0) + logg("EDNS(0) pheader is NULL"); return; + } // Debug logging if(config.debug & DEBUG_EDNS0) From 72b4bc5041ae7e48df4c338376151784b9f15d7b Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 9 Apr 2023 10:51:16 +0200 Subject: [PATCH 11/49] Analyse pseudeoheader before it might get stripped off Signed-off-by: DL6ER --- src/dnsmasq/forward.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/dnsmasq/forward.c b/src/dnsmasq/forward.c index 0725e216d..1f2eb2ddf 100644 --- a/src/dnsmasq/forward.c +++ b/src/dnsmasq/forward.c @@ -723,6 +723,10 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server /* Get extended RCODE. */ rcode |= sizep[2] << 4; + // Pi-hole modification: Interpret the pseudoheader before + // it might get stripped off below (added_pheader == true) + FTL_parse_pseudoheaders(pheader, (size_t)plen); + if (option_bool(OPT_CLIENT_SUBNET) && !check_source(header, plen, pheader, query_source)) { my_syslog(LOG_WARNING, _("discarding DNS reply: subnet option mismatch")); @@ -782,7 +786,6 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server union all_addr a; a.log.rcode = rcode; a.log.ede = ede; - FTL_parse_pseudoheaders(pheader, (size_t)plen); log_query(F_UPSTREAM | F_RCODE, "error", &a, NULL, 0); return resize_packet(header, n, pheader, plen); From b22524e54c7149ecb9c7c50085f962b845acdc65 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 9 Apr 2023 11:32:12 +0200 Subject: [PATCH 12/49] Initial DNSSEC status should be UNSPECIFIED Signed-off-by: DL6ER --- src/dnsmasq_interface.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/dnsmasq_interface.c b/src/dnsmasq_interface.c index 65af3796e..8a20e29ad 100644 --- a/src/dnsmasq_interface.c +++ b/src/dnsmasq_interface.c @@ -743,10 +743,6 @@ bool _FTL_new_query(const unsigned int flags, const char *name, counters->reply[REPLY_UNKNOWN]++; // Store DNSSEC result for this domain query->dnssec = DNSSEC_UNSPECIFIED; - // Every domain is insecure in the beginning. It can get secure or bogus - // only if validation reveals this. If DNSSEC validation is not used, the - // original status (DNSSEC_UNSPECIFIED) is not changed. - query_set_dnssec(query, DNSSEC_INSECURE); query->CNAME_domainID = -1; // This query is not yet known ad forwarded or blocked query->flags.blocked = false; From f216cb0b780d06e9efcbd6648c5dbd558269b4ad Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 9 Apr 2023 12:31:10 +0200 Subject: [PATCH 13/49] Explicitly set INSECURE status for replies received either from upstream (if they are not already validated as SECURE) or from cache. This is a direct consequence from the previous commit. Signed-off-by: DL6ER --- src/dnsmasq_interface.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/dnsmasq_interface.c b/src/dnsmasq_interface.c index 8a20e29ad..292396cb1 100644 --- a/src/dnsmasq_interface.c +++ b/src/dnsmasq_interface.c @@ -2105,6 +2105,10 @@ static void FTL_reply(const unsigned int flags, const char *name, const union al // Save reply type and update individual reply counters query_set_reply(flags, 0, addr, query, response); + // Set DNSSEC status to INSECURE if it is still unknown + if(query->dnssec == DNSSEC_UNSPECIFIED) + query_set_dnssec(query, DNSSEC_INSECURE); + // Hereby, this query is now fully determined query->flags.complete = true; } @@ -2144,6 +2148,12 @@ static void FTL_reply(const unsigned int flags, const char *name, const union al reply_flags = F_NEG; } } + else + { + // Set DNSSEC status to INSECURE if it is still unknown + if(query->dnssec == DNSSEC_UNSPECIFIED) + query_set_dnssec(query, DNSSEC_INSECURE); + } // Save reply type and update individual reply counters query_set_reply(reply_flags, 0, addr, query, response); From d228fbf3ad86d2402ad822c7c2fec0ee6227ea3e Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 9 Apr 2023 12:36:06 +0200 Subject: [PATCH 14/49] Apply the same logic also for reverse lookups (PTR) Signed-off-by: DL6ER --- src/dnsmasq_interface.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/dnsmasq_interface.c b/src/dnsmasq_interface.c index 292396cb1..e18b48309 100644 --- a/src/dnsmasq_interface.c +++ b/src/dnsmasq_interface.c @@ -2184,6 +2184,10 @@ static void FTL_reply(const unsigned int flags, const char *name, const union al // name = google-public-dns-a.google.com // Hence, isExactMatch is always false + // Set DNSSEC status to INSECURE if it is still unknown + if(query->dnssec == DNSSEC_UNSPECIFIED) + query_set_dnssec(query, DNSSEC_INSECURE); + // Save reply type and update individual reply counters query_set_reply(flags, 0, addr, query, response); } From d8271ad9b6fe5123d6303a43a8e27fcea145ea1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Sat, 15 Apr 2023 10:30:36 +0200 Subject: [PATCH 15/49] Trigger stale workflow on issue comments to remove stale label immediately MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Christian König --- .github/workflows/stale.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index e183ce17a..40698ff8f 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -4,6 +4,7 @@ on: schedule: - cron: '0 8 * * *' workflow_dispatch: + issue_comment: jobs: stale: From da8d0108e5bade1a3e42316dd298469ce3ed3a2e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 15 Apr 2023 10:57:01 +0000 Subject: [PATCH 16/49] Bump actions/checkout from 3.5.0 to 3.5.2 Bumps [actions/checkout](https://github.com/actions/checkout) from 3.5.0 to 3.5.2. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3.5.0...v3.5.2) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 6 +++--- .github/workflows/codespell.yml | 2 +- .github/workflows/sync-back-to-dev.yml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4815fc1db..824b16e95 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3.5.0 + uses: actions/checkout@v3.5.2 - name: "Calculate required variables" id: variables @@ -90,7 +90,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3.5.0 + uses: actions/checkout@v3.5.2 - name: "Fix ownership of repository" run: chown -R root . @@ -133,7 +133,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3.5.0 + uses: actions/checkout@v3.5.2 - name: Get Binaries built in previous jobs uses: actions/download-artifact@v3.0.2 diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index a04908f3a..bf2bcb45d 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -10,7 +10,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3.5.0 + uses: actions/checkout@v3.5.2 - name: Spell-Checking uses: codespell-project/actions-codespell@master diff --git a/.github/workflows/sync-back-to-dev.yml b/.github/workflows/sync-back-to-dev.yml index 79111dddb..a9e3a1642 100644 --- a/.github/workflows/sync-back-to-dev.yml +++ b/.github/workflows/sync-back-to-dev.yml @@ -11,7 +11,7 @@ jobs: name: Syncing branches steps: - name: Checkout - uses: actions/checkout@v3.5.0 + uses: actions/checkout@v3.5.2 - name: Opening pull request run: gh pr create -B development -H master --title 'Sync master back into development' --body 'Created by Github action' --label 'internal' env: From b5c1a845c82fda90843425918f8902b2d06191d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Sun, 16 Apr 2023 19:03:19 +0200 Subject: [PATCH 17/49] Run seperate job to trigger removal on comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Christian König --- .github/workflows/stale.yml | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 40698ff8f..8038de2bc 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -2,13 +2,13 @@ name: Mark stale issues on: schedule: - - cron: '0 8 * * *' + - cron: '0 8 * * *' workflow_dispatch: issue_comment: jobs: - stale: - + stale_action: + if: github.event_name != 'issue_comment' runs-on: ubuntu-latest permissions: issues: write @@ -25,3 +25,18 @@ jobs: exempt-all-issue-assignees: true operations-per-run: 300 close-issue-reason: 'not_planned' + + remove_stale: # trigger "stale" removal immediately when stale issues are commented on + if: github.event_name == 'issue_comment' + permissions: + contents: read # for actions/checkout + issues: write # to edit issues label + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3.4.0 + - name: Remove 'stale' label + run: gh issue edit ${{ github.event.issue.number }} --remove-label 'stale' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + From 71625df5e0acba88726bb4c334a06ef46414bd42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Mon, 17 Apr 2023 20:55:15 +0200 Subject: [PATCH 18/49] Use env variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Christian König --- .github/workflows/stale.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 8038de2bc..b53a27aeb 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -6,6 +6,9 @@ on: workflow_dispatch: issue_comment: +env: + stale_label: stale + jobs: stale_action: if: github.event_name != 'issue_comment' @@ -20,7 +23,7 @@ jobs: days-before-stale: 30 days-before-close: 5 stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Please comment or update this issue or it will be closed in 5 days.' - stale-issue-label: 'stale' + stale-issue-label: $stale_label exempt-issue-labels: 'Fixed in next release, Bug, Bug:Confirmed, Bugfix in progress, documentation needed, internal' exempt-all-issue-assignees: true operations-per-run: 300 @@ -36,7 +39,7 @@ jobs: - name: Checkout uses: actions/checkout@v3.4.0 - name: Remove 'stale' label - run: gh issue edit ${{ github.event.issue.number }} --remove-label 'stale' + run: gh issue edit ${{ github.event.issue.number }} --remove-label $stale_label env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 08a8a11a517a575d446effe4dd17210788e51f35 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 30 Apr 2023 21:10:44 +0200 Subject: [PATCH 19/49] Update embedded Lua to 5.4.5 Signed-off-by: DL6ER --- src/lua/lapi.c | 249 ++++++++++++++++++++++----------------------- src/lua/lapi.h | 17 ++-- src/lua/lauxlib.c | 56 +++++----- src/lua/lcode.c | 137 ++++++++++++++++--------- src/lua/lcorolib.c | 4 +- src/lua/ldebug.c | 80 ++++++++------- src/lua/ldebug.h | 2 +- src/lua/ldo.c | 229 +++++++++++++++++++++++------------------ src/lua/ldo.h | 17 +++- src/lua/ldump.c | 8 +- src/lua/lfunc.c | 54 +++++----- src/lua/lfunc.h | 6 +- src/lua/lgc.c | 106 ++++++++++--------- src/lua/lgc.h | 19 ++-- src/lua/linit.c | 3 - src/lua/llex.c | 6 +- src/lua/llimits.h | 21 +++- src/lua/lmathlib.c | 10 +- src/lua/lmem.c | 68 ++++++++----- src/lua/loadlib.c | 9 +- src/lua/lobject.c | 38 ++++--- src/lua/lobject.h | 19 +++- src/lua/lopcodes.h | 2 +- src/lua/loslib.c | 36 ++++--- src/lua/lparser.c | 31 +++--- src/lua/lstate.c | 57 +++++------ src/lua/lstate.h | 25 +++-- src/lua/lstrlib.c | 2 +- src/lua/ltable.c | 8 +- src/lua/ltable.h | 1 - src/lua/ltablib.c | 2 +- src/lua/ltm.c | 38 +++---- src/lua/ltm.h | 5 +- src/lua/lua.c | 57 +++++------ src/lua/lua.h | 26 +++-- src/lua/luac.c | 10 +- src/lua/luaconf.h | 13 ++- src/lua/lualib.h | 4 - src/lua/lundump.c | 8 +- src/lua/lutf8lib.c | 27 +++-- src/lua/lvm.c | 235 ++++++++++++++++++++++++++---------------- src/lua/lvm.h | 5 + 42 files changed, 991 insertions(+), 759 deletions(-) diff --git a/src/lua/lapi.c b/src/lua/lapi.c index 5ee65792d..34e64af14 100644 --- a/src/lua/lapi.c +++ b/src/lua/lapi.c @@ -60,27 +60,28 @@ const char lua_ident[] = static TValue *index2value (lua_State *L, int idx) { CallInfo *ci = L->ci; if (idx > 0) { - StkId o = ci->func + idx; - api_check(L, idx <= L->ci->top - (ci->func + 1), "unacceptable index"); - if (o >= L->top) return &G(L)->nilvalue; + StkId o = ci->func.p + idx; + api_check(L, idx <= ci->top.p - (ci->func.p + 1), "unacceptable index"); + if (o >= L->top.p) return &G(L)->nilvalue; else return s2v(o); } else if (!ispseudo(idx)) { /* negative index */ - api_check(L, idx != 0 && -idx <= L->top - (ci->func + 1), "invalid index"); - return s2v(L->top + idx); + api_check(L, idx != 0 && -idx <= L->top.p - (ci->func.p + 1), + "invalid index"); + return s2v(L->top.p + idx); } else if (idx == LUA_REGISTRYINDEX) return &G(L)->l_registry; else { /* upvalues */ idx = LUA_REGISTRYINDEX - idx; api_check(L, idx <= MAXUPVAL + 1, "upvalue index too large"); - if (ttisCclosure(s2v(ci->func))) { /* C closure? */ - CClosure *func = clCvalue(s2v(ci->func)); + if (ttisCclosure(s2v(ci->func.p))) { /* C closure? */ + CClosure *func = clCvalue(s2v(ci->func.p)); return (idx <= func->nupvalues) ? &func->upvalue[idx-1] : &G(L)->nilvalue; } else { /* light C function or Lua function (through a hook)?) */ - api_check(L, ttislcf(s2v(ci->func)), "caller not a C function"); + api_check(L, ttislcf(s2v(ci->func.p)), "caller not a C function"); return &G(L)->nilvalue; /* no upvalues */ } } @@ -94,14 +95,15 @@ static TValue *index2value (lua_State *L, int idx) { l_sinline StkId index2stack (lua_State *L, int idx) { CallInfo *ci = L->ci; if (idx > 0) { - StkId o = ci->func + idx; - api_check(L, o < L->top, "invalid index"); + StkId o = ci->func.p + idx; + api_check(L, o < L->top.p, "invalid index"); return o; } else { /* non-positive index */ - api_check(L, idx != 0 && -idx <= L->top - (ci->func + 1), "invalid index"); + api_check(L, idx != 0 && -idx <= L->top.p - (ci->func.p + 1), + "invalid index"); api_check(L, !ispseudo(idx), "invalid index"); - return L->top + idx; + return L->top.p + idx; } } @@ -112,17 +114,12 @@ LUA_API int lua_checkstack (lua_State *L, int n) { lua_lock(L); ci = L->ci; api_check(L, n >= 0, "negative 'n'"); - if (L->stack_last - L->top > n) /* stack large enough? */ + if (L->stack_last.p - L->top.p > n) /* stack large enough? */ res = 1; /* yes; check is OK */ - else { /* no; need to grow stack */ - int inuse = cast_int(L->top - L->stack) + EXTRA_STACK; - if (inuse > LUAI_MAXSTACK - n) /* can grow without overflow? */ - res = 0; /* no */ - else /* try to grow stack */ - res = luaD_growstack(L, n, 0); - } - if (res && ci->top < L->top + n) - ci->top = L->top + n; /* adjust frame top */ + else /* need to grow stack */ + res = luaD_growstack(L, n, 0); + if (res && ci->top.p < L->top.p + n) + ci->top.p = L->top.p + n; /* adjust frame top */ lua_unlock(L); return res; } @@ -134,11 +131,11 @@ LUA_API void lua_xmove (lua_State *from, lua_State *to, int n) { lua_lock(to); api_checknelems(from, n); api_check(from, G(from) == G(to), "moving among independent states"); - api_check(from, to->ci->top - to->top >= n, "stack overflow"); - from->top -= n; + api_check(from, to->ci->top.p - to->top.p >= n, "stack overflow"); + from->top.p -= n; for (i = 0; i < n; i++) { - setobjs2s(to, to->top, from->top + i); - to->top++; /* stack already checked by previous 'api_check' */ + setobjs2s(to, to->top.p, from->top.p + i); + to->top.p++; /* stack already checked by previous 'api_check' */ } lua_unlock(to); } @@ -172,12 +169,12 @@ LUA_API lua_Number lua_version (lua_State *L) { LUA_API int lua_absindex (lua_State *L, int idx) { return (idx > 0 || ispseudo(idx)) ? idx - : cast_int(L->top - L->ci->func) + idx; + : cast_int(L->top.p - L->ci->func.p) + idx; } LUA_API int lua_gettop (lua_State *L) { - return cast_int(L->top - (L->ci->func + 1)); + return cast_int(L->top.p - (L->ci->func.p + 1)); } @@ -187,24 +184,24 @@ LUA_API void lua_settop (lua_State *L, int idx) { ptrdiff_t diff; /* difference for new top */ lua_lock(L); ci = L->ci; - func = ci->func; + func = ci->func.p; if (idx >= 0) { - api_check(L, idx <= ci->top - (func + 1), "new top too large"); - diff = ((func + 1) + idx) - L->top; + api_check(L, idx <= ci->top.p - (func + 1), "new top too large"); + diff = ((func + 1) + idx) - L->top.p; for (; diff > 0; diff--) - setnilvalue(s2v(L->top++)); /* clear new slots */ + setnilvalue(s2v(L->top.p++)); /* clear new slots */ } else { - api_check(L, -(idx+1) <= (L->top - (func + 1)), "invalid new top"); + api_check(L, -(idx+1) <= (L->top.p - (func + 1)), "invalid new top"); diff = idx + 1; /* will "subtract" index (as it is negative) */ } - api_check(L, L->tbclist < L->top, "previous pop of an unclosed slot"); - newtop = L->top + diff; - if (diff < 0 && L->tbclist >= newtop) { + api_check(L, L->tbclist.p < L->top.p, "previous pop of an unclosed slot"); + newtop = L->top.p + diff; + if (diff < 0 && L->tbclist.p >= newtop) { lua_assert(hastocloseCfunc(ci->nresults)); - luaF_close(L, newtop, CLOSEKTOP, 0); + newtop = luaF_close(L, newtop, CLOSEKTOP, 0); } - L->top = newtop; /* correct top only after closing any upvalue */ + L->top.p = newtop; /* correct top only after closing any upvalue */ lua_unlock(L); } @@ -213,10 +210,9 @@ LUA_API void lua_closeslot (lua_State *L, int idx) { StkId level; lua_lock(L); level = index2stack(L, idx); - api_check(L, hastocloseCfunc(L->ci->nresults) && L->tbclist == level, + api_check(L, hastocloseCfunc(L->ci->nresults) && L->tbclist.p == level, "no variable to close at given level"); - luaF_close(L, level, CLOSEKTOP, 0); - level = index2stack(L, idx); /* stack may be moved */ + level = luaF_close(L, level, CLOSEKTOP, 0); setnilvalue(s2v(level)); lua_unlock(L); } @@ -245,7 +241,7 @@ l_sinline void reverse (lua_State *L, StkId from, StkId to) { LUA_API void lua_rotate (lua_State *L, int idx, int n) { StkId p, t, m; lua_lock(L); - t = L->top - 1; /* end of stack segment being rotated */ + t = L->top.p - 1; /* end of stack segment being rotated */ p = index2stack(L, idx); /* start of segment */ api_check(L, (n >= 0 ? n : -n) <= (t - p + 1), "invalid 'n'"); m = (n >= 0 ? t - n : p - n - 1); /* end of prefix */ @@ -264,7 +260,7 @@ LUA_API void lua_copy (lua_State *L, int fromidx, int toidx) { api_check(L, isvalid(L, to), "invalid index"); setobj(L, to, fr); if (isupvalue(toidx)) /* function upvalue? */ - luaC_barrier(L, clCvalue(s2v(L->ci->func)), fr); + luaC_barrier(L, clCvalue(s2v(L->ci->func.p)), fr); /* LUA_REGISTRYINDEX does not need gc barrier (collector revisits it before finishing collection) */ lua_unlock(L); @@ -273,7 +269,7 @@ LUA_API void lua_copy (lua_State *L, int fromidx, int toidx) { LUA_API void lua_pushvalue (lua_State *L, int idx) { lua_lock(L); - setobj2s(L, L->top, index2value(L, idx)); + setobj2s(L, L->top.p, index2value(L, idx)); api_incr_top(L); lua_unlock(L); } @@ -342,12 +338,12 @@ LUA_API void lua_arith (lua_State *L, int op) { api_checknelems(L, 2); /* all other operations expect two operands */ else { /* for unary operations, add fake 2nd operand */ api_checknelems(L, 1); - setobjs2s(L, L->top, L->top - 1); + setobjs2s(L, L->top.p, L->top.p - 1); api_incr_top(L); } /* first operand at top - 2, second at top - 1; result go to top - 2 */ - luaO_arith(L, op, s2v(L->top - 2), s2v(L->top - 1), L->top - 2); - L->top--; /* remove second operand */ + luaO_arith(L, op, s2v(L->top.p - 2), s2v(L->top.p - 1), L->top.p - 2); + L->top.p--; /* remove second operand */ lua_unlock(L); } @@ -373,7 +369,7 @@ LUA_API int lua_compare (lua_State *L, int index1, int index2, int op) { LUA_API size_t lua_stringtonumber (lua_State *L, const char *s) { - size_t sz = luaO_str2num(s, s2v(L->top)); + size_t sz = luaO_str2num(s, s2v(L->top.p)); if (sz != 0) api_incr_top(L); return sz; @@ -500,7 +496,7 @@ LUA_API const void *lua_topointer (lua_State *L, int idx) { LUA_API void lua_pushnil (lua_State *L) { lua_lock(L); - setnilvalue(s2v(L->top)); + setnilvalue(s2v(L->top.p)); api_incr_top(L); lua_unlock(L); } @@ -508,7 +504,7 @@ LUA_API void lua_pushnil (lua_State *L) { LUA_API void lua_pushnumber (lua_State *L, lua_Number n) { lua_lock(L); - setfltvalue(s2v(L->top), n); + setfltvalue(s2v(L->top.p), n); api_incr_top(L); lua_unlock(L); } @@ -516,7 +512,7 @@ LUA_API void lua_pushnumber (lua_State *L, lua_Number n) { LUA_API void lua_pushinteger (lua_State *L, lua_Integer n) { lua_lock(L); - setivalue(s2v(L->top), n); + setivalue(s2v(L->top.p), n); api_incr_top(L); lua_unlock(L); } @@ -531,7 +527,7 @@ LUA_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len) { TString *ts; lua_lock(L); ts = (len == 0) ? luaS_new(L, "") : luaS_newlstr(L, s, len); - setsvalue2s(L, L->top, ts); + setsvalue2s(L, L->top.p, ts); api_incr_top(L); luaC_checkGC(L); lua_unlock(L); @@ -542,11 +538,11 @@ LUA_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len) { LUA_API const char *lua_pushstring (lua_State *L, const char *s) { lua_lock(L); if (s == NULL) - setnilvalue(s2v(L->top)); + setnilvalue(s2v(L->top.p)); else { TString *ts; ts = luaS_new(L, s); - setsvalue2s(L, L->top, ts); + setsvalue2s(L, L->top.p, ts); s = getstr(ts); /* internal copy's address */ } api_incr_top(L); @@ -583,7 +579,7 @@ 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_lock(L); if (n == 0) { - setfvalue(s2v(L->top), fn); + setfvalue(s2v(L->top.p), fn); api_incr_top(L); } else { @@ -592,13 +588,13 @@ LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) { api_check(L, n <= MAXUPVAL, "upvalue index too large"); cl = luaF_newCclosure(L, n); cl->f = fn; - L->top -= n; + L->top.p -= n; while (n--) { - setobj2n(L, &cl->upvalue[n], s2v(L->top + n)); + setobj2n(L, &cl->upvalue[n], s2v(L->top.p + n)); /* does not need barrier because closure is white */ lua_assert(iswhite(cl)); } - setclCvalue(L, s2v(L->top), cl); + setclCvalue(L, s2v(L->top.p), cl); api_incr_top(L); luaC_checkGC(L); } @@ -609,9 +605,9 @@ LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) { LUA_API void lua_pushboolean (lua_State *L, int b) { lua_lock(L); if (b) - setbtvalue(s2v(L->top)); + setbtvalue(s2v(L->top.p)); else - setbfvalue(s2v(L->top)); + setbfvalue(s2v(L->top.p)); api_incr_top(L); lua_unlock(L); } @@ -619,7 +615,7 @@ LUA_API void lua_pushboolean (lua_State *L, int b) { LUA_API void lua_pushlightuserdata (lua_State *L, void *p) { lua_lock(L); - setpvalue(s2v(L->top), p); + setpvalue(s2v(L->top.p), p); api_incr_top(L); lua_unlock(L); } @@ -627,7 +623,7 @@ LUA_API void lua_pushlightuserdata (lua_State *L, void *p) { LUA_API int lua_pushthread (lua_State *L) { lua_lock(L); - setthvalue(L, s2v(L->top), L); + setthvalue(L, s2v(L->top.p), L); api_incr_top(L); lua_unlock(L); return (G(L)->mainthread == L); @@ -644,16 +640,16 @@ l_sinline int auxgetstr (lua_State *L, const TValue *t, const char *k) { const TValue *slot; TString *str = luaS_new(L, k); if (luaV_fastget(L, t, str, slot, luaH_getstr)) { - setobj2s(L, L->top, slot); + setobj2s(L, L->top.p, slot); api_incr_top(L); } else { - setsvalue2s(L, L->top, str); + setsvalue2s(L, L->top.p, str); api_incr_top(L); - luaV_finishget(L, t, s2v(L->top - 1), L->top - 1, slot); + luaV_finishget(L, t, s2v(L->top.p - 1), L->top.p - 1, slot); } lua_unlock(L); - return ttype(s2v(L->top - 1)); + return ttype(s2v(L->top.p - 1)); } @@ -680,13 +676,13 @@ LUA_API int lua_gettable (lua_State *L, int idx) { TValue *t; lua_lock(L); t = index2value(L, idx); - if (luaV_fastget(L, t, s2v(L->top - 1), slot, luaH_get)) { - setobj2s(L, L->top - 1, slot); + if (luaV_fastget(L, t, s2v(L->top.p - 1), slot, luaH_get)) { + setobj2s(L, L->top.p - 1, slot); } else - luaV_finishget(L, t, s2v(L->top - 1), L->top - 1, slot); + luaV_finishget(L, t, s2v(L->top.p - 1), L->top.p - 1, slot); lua_unlock(L); - return ttype(s2v(L->top - 1)); + return ttype(s2v(L->top.p - 1)); } @@ -702,27 +698,27 @@ LUA_API int lua_geti (lua_State *L, int idx, lua_Integer n) { lua_lock(L); t = index2value(L, idx); if (luaV_fastgeti(L, t, n, slot)) { - setobj2s(L, L->top, slot); + setobj2s(L, L->top.p, slot); } else { TValue aux; setivalue(&aux, n); - luaV_finishget(L, t, &aux, L->top, slot); + luaV_finishget(L, t, &aux, L->top.p, slot); } api_incr_top(L); lua_unlock(L); - return ttype(s2v(L->top - 1)); + return ttype(s2v(L->top.p - 1)); } l_sinline int finishrawget (lua_State *L, const TValue *val) { if (isempty(val)) /* avoid copying empty items to the stack */ - setnilvalue(s2v(L->top)); + setnilvalue(s2v(L->top.p)); else - setobj2s(L, L->top, val); + setobj2s(L, L->top.p, val); api_incr_top(L); lua_unlock(L); - return ttype(s2v(L->top - 1)); + return ttype(s2v(L->top.p - 1)); } @@ -739,8 +735,8 @@ LUA_API int lua_rawget (lua_State *L, int idx) { lua_lock(L); api_checknelems(L, 1); t = gettable(L, idx); - val = luaH_get(t, s2v(L->top - 1)); - L->top--; /* remove key */ + val = luaH_get(t, s2v(L->top.p - 1)); + L->top.p--; /* remove key */ return finishrawget(L, val); } @@ -767,7 +763,7 @@ LUA_API void lua_createtable (lua_State *L, int narray, int nrec) { Table *t; lua_lock(L); t = luaH_new(L); - sethvalue2s(L, L->top, t); + sethvalue2s(L, L->top.p, t); api_incr_top(L); if (narray > 0 || nrec > 0) luaH_resize(L, t, narray, nrec); @@ -794,7 +790,7 @@ LUA_API int lua_getmetatable (lua_State *L, int objindex) { break; } if (mt != NULL) { - sethvalue2s(L, L->top, mt); + sethvalue2s(L, L->top.p, mt); api_incr_top(L); res = 1; } @@ -810,12 +806,12 @@ LUA_API int lua_getiuservalue (lua_State *L, int idx, int n) { o = index2value(L, idx); api_check(L, ttisfulluserdata(o), "full userdata expected"); if (n <= 0 || n > uvalue(o)->nuvalue) { - setnilvalue(s2v(L->top)); + setnilvalue(s2v(L->top.p)); t = LUA_TNONE; } else { - setobj2s(L, L->top, &uvalue(o)->uv[n - 1].uv); - t = ttype(s2v(L->top)); + setobj2s(L, L->top.p, &uvalue(o)->uv[n - 1].uv); + t = ttype(s2v(L->top.p)); } api_incr_top(L); lua_unlock(L); @@ -835,14 +831,14 @@ static void auxsetstr (lua_State *L, const TValue *t, const char *k) { TString *str = luaS_new(L, k); api_checknelems(L, 1); if (luaV_fastget(L, t, str, slot, luaH_getstr)) { - luaV_finishfastset(L, t, slot, s2v(L->top - 1)); - L->top--; /* pop value */ + luaV_finishfastset(L, t, slot, s2v(L->top.p - 1)); + L->top.p--; /* pop value */ } else { - setsvalue2s(L, L->top, str); /* push 'str' (to make it a TValue) */ + setsvalue2s(L, L->top.p, str); /* push 'str' (to make it a TValue) */ api_incr_top(L); - luaV_finishset(L, t, s2v(L->top - 1), s2v(L->top - 2), slot); - L->top -= 2; /* pop value and key */ + luaV_finishset(L, t, s2v(L->top.p - 1), s2v(L->top.p - 2), slot); + L->top.p -= 2; /* pop value and key */ } lua_unlock(L); /* lock done by caller */ } @@ -862,12 +858,12 @@ LUA_API void lua_settable (lua_State *L, int idx) { lua_lock(L); api_checknelems(L, 2); t = index2value(L, idx); - if (luaV_fastget(L, t, s2v(L->top - 2), slot, luaH_get)) { - luaV_finishfastset(L, t, slot, s2v(L->top - 1)); + if (luaV_fastget(L, t, s2v(L->top.p - 2), slot, luaH_get)) { + luaV_finishfastset(L, t, slot, s2v(L->top.p - 1)); } else - luaV_finishset(L, t, s2v(L->top - 2), s2v(L->top - 1), slot); - L->top -= 2; /* pop index and value */ + luaV_finishset(L, t, s2v(L->top.p - 2), s2v(L->top.p - 1), slot); + L->top.p -= 2; /* pop index and value */ lua_unlock(L); } @@ -885,14 +881,14 @@ LUA_API void lua_seti (lua_State *L, int idx, lua_Integer n) { api_checknelems(L, 1); t = index2value(L, idx); if (luaV_fastgeti(L, t, n, slot)) { - luaV_finishfastset(L, t, slot, s2v(L->top - 1)); + luaV_finishfastset(L, t, slot, s2v(L->top.p - 1)); } else { TValue aux; setivalue(&aux, n); - luaV_finishset(L, t, &aux, s2v(L->top - 1), slot); + luaV_finishset(L, t, &aux, s2v(L->top.p - 1), slot); } - L->top--; /* pop value */ + L->top.p--; /* pop value */ lua_unlock(L); } @@ -902,16 +898,16 @@ static void aux_rawset (lua_State *L, int idx, TValue *key, int n) { lua_lock(L); api_checknelems(L, n); t = gettable(L, idx); - luaH_set(L, t, key, s2v(L->top - 1)); + luaH_set(L, t, key, s2v(L->top.p - 1)); invalidateTMcache(t); - luaC_barrierback(L, obj2gco(t), s2v(L->top - 1)); - L->top -= n; + luaC_barrierback(L, obj2gco(t), s2v(L->top.p - 1)); + L->top.p -= n; lua_unlock(L); } LUA_API void lua_rawset (lua_State *L, int idx) { - aux_rawset(L, idx, s2v(L->top - 2), 2); + aux_rawset(L, idx, s2v(L->top.p - 2), 2); } @@ -927,9 +923,9 @@ LUA_API void lua_rawseti (lua_State *L, int idx, lua_Integer n) { lua_lock(L); api_checknelems(L, 1); t = gettable(L, idx); - luaH_setint(L, t, n, s2v(L->top - 1)); - luaC_barrierback(L, obj2gco(t), s2v(L->top - 1)); - L->top--; + luaH_setint(L, t, n, s2v(L->top.p - 1)); + luaC_barrierback(L, obj2gco(t), s2v(L->top.p - 1)); + L->top.p--; lua_unlock(L); } @@ -940,11 +936,11 @@ LUA_API int lua_setmetatable (lua_State *L, int objindex) { lua_lock(L); api_checknelems(L, 1); obj = index2value(L, objindex); - if (ttisnil(s2v(L->top - 1))) + if (ttisnil(s2v(L->top.p - 1))) mt = NULL; else { - api_check(L, ttistable(s2v(L->top - 1)), "table expected"); - mt = hvalue(s2v(L->top - 1)); + api_check(L, ttistable(s2v(L->top.p - 1)), "table expected"); + mt = hvalue(s2v(L->top.p - 1)); } switch (ttype(obj)) { case LUA_TTABLE: { @@ -968,7 +964,7 @@ LUA_API int lua_setmetatable (lua_State *L, int objindex) { break; } } - L->top--; + L->top.p--; lua_unlock(L); return 1; } @@ -984,11 +980,11 @@ LUA_API int lua_setiuservalue (lua_State *L, int idx, int n) { if (!(cast_uint(n) - 1u < cast_uint(uvalue(o)->nuvalue))) res = 0; /* 'n' not in [1, uvalue(o)->nuvalue] */ else { - setobj(L, &uvalue(o)->uv[n - 1].uv, s2v(L->top - 1)); - luaC_barrierback(L, gcvalue(o), s2v(L->top - 1)); + setobj(L, &uvalue(o)->uv[n - 1].uv, s2v(L->top.p - 1)); + luaC_barrierback(L, gcvalue(o), s2v(L->top.p - 1)); res = 1; } - L->top--; + L->top.p--; lua_unlock(L); return res; } @@ -1000,7 +996,8 @@ LUA_API int lua_setiuservalue (lua_State *L, int idx, int n) { #define checkresults(L,na,nr) \ - api_check(L, (nr) == LUA_MULTRET || (L->ci->top - L->top >= (nr) - (na)), \ + api_check(L, (nr) == LUA_MULTRET \ + || (L->ci->top.p - L->top.p >= (nr) - (na)), \ "results from function overflow current stack size") @@ -1013,7 +1010,7 @@ LUA_API void lua_callk (lua_State *L, int nargs, int nresults, api_checknelems(L, nargs+1); api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread"); checkresults(L, nargs, nresults); - func = L->top - (nargs+1); + func = L->top.p - (nargs+1); if (k != NULL && yieldable(L)) { /* need to prepare continuation? */ L->ci->u.c.k = k; /* save continuation */ L->ci->u.c.ctx = ctx; /* save context */ @@ -1061,7 +1058,7 @@ LUA_API int lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc, api_check(L, ttisfunction(s2v(o)), "error handler must be a function"); func = savestack(L, o); } - c.func = L->top - (nargs+1); /* function to be called */ + c.func = L->top.p - (nargs+1); /* function to be called */ if (k == NULL || !yieldable(L)) { /* no continuation or no yieldable? */ c.nresults = nresults; /* do a 'conventional' protected call */ status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func); @@ -1096,12 +1093,12 @@ LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data, luaZ_init(L, &z, reader, data); status = luaD_protectedparser(L, &z, chunkname, mode); if (status == LUA_OK) { /* no errors? */ - LClosure *f = clLvalue(s2v(L->top - 1)); /* get newly created function */ + LClosure *f = clLvalue(s2v(L->top.p - 1)); /* get new function */ if (f->nupvalues >= 1) { /* does it have an upvalue? */ /* get global table from registry */ const TValue *gt = getGtable(L); /* set global table as 1st upvalue of 'f' (may be LUA_ENV) */ - setobj(L, f->upvals[0]->v, gt); + setobj(L, f->upvals[0]->v.p, gt); luaC_barrier(L, f->upvals[0], gt); } } @@ -1115,7 +1112,7 @@ LUA_API int lua_dump (lua_State *L, lua_Writer writer, void *data, int strip) { TValue *o; lua_lock(L); api_checknelems(L, 1); - o = s2v(L->top - 1); + o = s2v(L->top.p - 1); if (isLfunction(o)) status = luaU_dump(L, getproto(o), writer, data, strip); else @@ -1241,7 +1238,7 @@ LUA_API int lua_gc (lua_State *L, int what, ...) { LUA_API int lua_error (lua_State *L) { TValue *errobj; lua_lock(L); - errobj = s2v(L->top - 1); + errobj = s2v(L->top.p - 1); api_checknelems(L, 1); /* error object is the memory error message? */ if (ttisshrstring(errobj) && eqshrstr(tsvalue(errobj), G(L)->memerrmsg)) @@ -1259,12 +1256,12 @@ LUA_API int lua_next (lua_State *L, int idx) { lua_lock(L); api_checknelems(L, 1); t = gettable(L, idx); - more = luaH_next(L, t, L->top - 1); + more = luaH_next(L, t, L->top.p - 1); if (more) { api_incr_top(L); } else /* no more elements */ - L->top -= 1; /* remove key */ + L->top.p -= 1; /* remove key */ lua_unlock(L); return more; } @@ -1276,7 +1273,7 @@ LUA_API void lua_toclose (lua_State *L, int idx) { lua_lock(L); o = index2stack(L, idx); nresults = L->ci->nresults; - api_check(L, L->tbclist < o, "given index below or equal a marked one"); + api_check(L, L->tbclist.p < o, "given index below or equal a marked one"); luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */ if (!hastocloseCfunc(nresults)) /* function not marked yet? */ L->ci->nresults = codeNresults(nresults); /* mark it */ @@ -1291,7 +1288,7 @@ LUA_API void lua_concat (lua_State *L, int n) { if (n > 0) luaV_concat(L, n); else { /* nothing to concatenate */ - setsvalue2s(L, L->top, luaS_newlstr(L, "", 0)); /* push empty string */ + setsvalue2s(L, L->top.p, luaS_newlstr(L, "", 0)); /* push empty string */ api_incr_top(L); } luaC_checkGC(L); @@ -1303,7 +1300,7 @@ LUA_API void lua_len (lua_State *L, int idx) { TValue *t; lua_lock(L); t = index2value(L, idx); - luaV_objlen(L, L->top, t); + luaV_objlen(L, L->top.p, t); api_incr_top(L); lua_unlock(L); } @@ -1348,7 +1345,7 @@ LUA_API void *lua_newuserdatauv (lua_State *L, size_t size, int nuvalue) { lua_lock(L); api_check(L, 0 <= nuvalue && nuvalue < USHRT_MAX, "invalid value"); u = luaS_newudata(L, size, nuvalue); - setuvalue(L, s2v(L->top), u); + setuvalue(L, s2v(L->top.p), u); api_incr_top(L); luaC_checkGC(L); lua_unlock(L); @@ -1374,7 +1371,7 @@ static const char *aux_upvalue (TValue *fi, int n, TValue **val, Proto *p = f->p; if (!(cast_uint(n) - 1u < cast_uint(p->sizeupvalues))) return NULL; /* 'n' not in [1, p->sizeupvalues] */ - *val = f->upvals[n-1]->v; + *val = f->upvals[n-1]->v.p; if (owner) *owner = obj2gco(f->upvals[n - 1]); name = p->upvalues[n-1].name; return (name == NULL) ? "(no name)" : getstr(name); @@ -1390,7 +1387,7 @@ LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n) { lua_lock(L); name = aux_upvalue(index2value(L, funcindex), n, &val, NULL); if (name) { - setobj2s(L, L->top, val); + setobj2s(L, L->top.p, val); api_incr_top(L); } lua_unlock(L); @@ -1408,8 +1405,8 @@ LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) { api_checknelems(L, 1); name = aux_upvalue(fi, n, &val, &owner); if (name) { - L->top--; - setobj(L, val, s2v(L->top)); + L->top.p--; + setobj(L, val, s2v(L->top.p)); luaC_barrier(L, owner, val); } lua_unlock(L); diff --git a/src/lua/lapi.h b/src/lua/lapi.h index 9e99cc448..a742427cd 100644 --- a/src/lua/lapi.h +++ b/src/lua/lapi.h @@ -12,23 +12,26 @@ #include "lstate.h" -/* Increments 'L->top', checking for stack overflows */ -#define api_incr_top(L) {L->top++; api_check(L, L->top <= L->ci->top, \ - "stack overflow");} +/* Increments 'L->top.p', checking for stack overflows */ +#define api_incr_top(L) {L->top.p++; \ + api_check(L, L->top.p <= L->ci->top.p, \ + "stack overflow");} /* ** If a call returns too many multiple returns, the callee may not have ** stack space to accommodate all results. In this case, this macro -** increases its stack space ('L->ci->top'). +** increases its stack space ('L->ci->top.p'). */ #define adjustresults(L,nres) \ - { if ((nres) <= LUA_MULTRET && L->ci->top < L->top) L->ci->top = L->top; } + { if ((nres) <= LUA_MULTRET && L->ci->top.p < L->top.p) \ + L->ci->top.p = L->top.p; } /* Ensure the stack has at least 'n' elements */ -#define api_checknelems(L,n) api_check(L, (n) < (L->top - L->ci->func), \ - "not enough elements in the stack") +#define api_checknelems(L,n) \ + api_check(L, (n) < (L->top.p - L->ci->func.p), \ + "not enough elements in the stack") /* diff --git a/src/lua/lauxlib.c b/src/lua/lauxlib.c index 8ed1da112..4ca6c6548 100644 --- a/src/lua/lauxlib.c +++ b/src/lua/lauxlib.c @@ -526,13 +526,14 @@ static void newbox (lua_State *L) { /* ** Compute new size for buffer 'B', enough to accommodate extra 'sz' -** bytes. +** bytes. (The test for "not big enough" also gets the case when the +** computation of 'newsize' overflows.) */ static size_t newbuffsize (luaL_Buffer *B, size_t sz) { - size_t newsize = B->size * 2; /* double buffer size */ + size_t newsize = (B->size / 2) * 3; /* buffer size * 1.5 */ if (l_unlikely(MAX_SIZET - sz < B->n)) /* overflow in (B->n + sz)? */ return luaL_error(B->L, "buffer too large"); - if (newsize < B->n + sz) /* double is not big enough? */ + if (newsize < B->n + sz) /* not big enough? */ newsize = B->n + sz; return newsize; } @@ -611,7 +612,7 @@ LUALIB_API void luaL_pushresultsize (luaL_Buffer *B, size_t sz) { ** box (if existent) is not on the top of the stack. So, instead of ** calling 'luaL_addlstring', it replicates the code using -2 as the ** last argument to 'prepbuffsize', signaling that the box is (or will -** be) bellow the string being added to the buffer. (Box creation can +** be) below the string being added to the buffer. (Box creation can ** trigger an emergency GC, so we should not remove the string from the ** stack before we have the space guaranteed.) */ @@ -739,17 +740,18 @@ static int errfile (lua_State *L, const char *what, int fnameindex) { } -static int skipBOM (LoadF *lf) { - const char *p = "\xEF\xBB\xBF"; /* UTF-8 BOM mark */ - int c; - lf->n = 0; - do { - c = getc(lf->f); - if (c == EOF || c != *(const unsigned char *)p++) return c; - lf->buff[lf->n++] = c; /* to be read by the parser */ - } while (*p != '\0'); - lf->n = 0; /* prefix matched; discard it */ - return getc(lf->f); /* return next character */ +/* +** Skip an optional BOM at the start of a stream. If there is an +** incomplete BOM (the first character is correct but the rest is +** not), returns the first character anyway to force an error +** (as no chunk can start with 0xEF). +*/ +static int skipBOM (FILE *f) { + int c = getc(f); /* read first character */ + if (c == 0xEF && getc(f) == 0xBB && getc(f) == 0xBF) /* correct BOM? */ + return getc(f); /* ignore BOM and return next char */ + else /* no (valid) BOM */ + return c; /* return first character */ } @@ -760,13 +762,13 @@ static int skipBOM (LoadF *lf) { ** first "valid" character of the file (after the optional BOM and ** a first-line comment). */ -static int skipcomment (LoadF *lf, int *cp) { - int c = *cp = skipBOM(lf); +static int skipcomment (FILE *f, int *cp) { + int c = *cp = skipBOM(f); if (c == '#') { /* first line is a comment (Unix exec. file)? */ do { /* skip first line */ - c = getc(lf->f); + c = getc(f); } while (c != EOF && c != '\n'); - *cp = getc(lf->f); /* skip end-of-line, if present */ + *cp = getc(f); /* next character after comment, if present */ return 1; /* there was a comment */ } else return 0; /* no comment */ @@ -788,12 +790,16 @@ LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename, lf.f = fopen(filename, "r"); if (lf.f == NULL) return errfile(L, "open", fnameindex); } - if (skipcomment(&lf, &c)) /* read initial portion */ - lf.buff[lf.n++] = '\n'; /* add line to correct line numbers */ - 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); - skipcomment(&lf, &c); /* re-read initial portion */ + lf.n = 0; + if (skipcomment(lf.f, &c)) /* read initial portion */ + lf.buff[lf.n++] = '\n'; /* add newline to correct line numbers */ + if (c == LUA_SIGNATURE[0]) { /* binary file? */ + lf.n = 0; /* remove possible newline */ + if (filename) { /* "real" file? */ + lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */ + if (lf.f == NULL) return errfile(L, "reopen", fnameindex); + skipcomment(lf.f, &c); /* re-read initial portion */ + } } if (c != EOF) lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */ diff --git a/src/lua/lcode.c b/src/lua/lcode.c index 06425a1db..1a371ca94 100644 --- a/src/lua/lcode.c +++ b/src/lua/lcode.c @@ -1351,6 +1351,35 @@ static int constfolding (FuncState *fs, int op, expdesc *e1, } +/* +** Convert a BinOpr to an OpCode (ORDER OPR - ORDER OP) +*/ +l_sinline OpCode binopr2op (BinOpr opr, BinOpr baser, OpCode base) { + lua_assert(baser <= opr && + ((baser == OPR_ADD && opr <= OPR_SHR) || + (baser == OPR_LT && opr <= OPR_LE))); + return cast(OpCode, (cast_int(opr) - cast_int(baser)) + cast_int(base)); +} + + +/* +** Convert a UnOpr to an OpCode (ORDER OPR - ORDER OP) +*/ +l_sinline OpCode unopr2op (UnOpr opr) { + return cast(OpCode, (cast_int(opr) - cast_int(OPR_MINUS)) + + cast_int(OP_UNM)); +} + + +/* +** Convert a BinOpr to a tag method (ORDER OPR - ORDER TM) +*/ +l_sinline TMS binopr2TM (BinOpr opr) { + lua_assert(OPR_ADD <= opr && opr <= OPR_SHR); + return cast(TMS, (cast_int(opr) - cast_int(OPR_ADD)) + cast_int(TM_ADD)); +} + + /* ** Emit code for unary expressions that "produce values" ** (everything but 'not'). @@ -1389,12 +1418,15 @@ static void finishbinexpval (FuncState *fs, expdesc *e1, expdesc *e2, ** Emit code for binary expressions that "produce values" over ** two registers. */ -static void codebinexpval (FuncState *fs, OpCode op, +static void codebinexpval (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2, int line) { - int v2 = luaK_exp2anyreg(fs, e2); /* both operands are in registers */ + OpCode op = binopr2op(opr, OPR_ADD, OP_ADD); + int v2 = luaK_exp2anyreg(fs, e2); /* make sure 'e2' is in a register */ + /* 'e1' must be already in a register or it is a constant */ + lua_assert((VNIL <= e1->k && e1->k <= VKSTR) || + e1->k == VNONRELOC || e1->k == VRELOC); lua_assert(OP_ADD <= op && op <= OP_SHR); - finishbinexpval(fs, e1, e2, op, v2, 0, line, OP_MMBIN, - cast(TMS, (op - OP_ADD) + TM_ADD)); + finishbinexpval(fs, e1, e2, op, v2, 0, line, OP_MMBIN, binopr2TM(opr)); } @@ -1410,6 +1442,18 @@ static void codebini (FuncState *fs, OpCode op, } +/* +** Code binary operators with K operand. +*/ +static void codebinK (FuncState *fs, BinOpr opr, + expdesc *e1, expdesc *e2, int flip, int line) { + TMS event = binopr2TM(opr); + int v2 = e2->u.info; /* K index */ + OpCode op = binopr2op(opr, OPR_ADD, OP_ADDK); + finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINK, event); +} + + /* Try to code a binary operator negating its second operand. ** For the metamethod, 2nd operand must keep its original value. */ @@ -1437,24 +1481,27 @@ static void swapexps (expdesc *e1, expdesc *e2) { } +/* +** Code binary operators with no constant operand. +*/ +static void codebinNoK (FuncState *fs, BinOpr opr, + expdesc *e1, expdesc *e2, int flip, int line) { + if (flip) + swapexps(e1, e2); /* back to original order */ + codebinexpval(fs, opr, e1, e2, line); /* use standard operators */ +} + + /* ** Code arithmetic operators ('+', '-', ...). If second operand is a ** constant in the proper range, use variant opcodes with K operands. */ static void codearith (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2, int flip, int line) { - TMS event = cast(TMS, opr + TM_ADD); - if (tonumeral(e2, NULL) && luaK_exp2K(fs, e2)) { /* K operand? */ - int v2 = e2->u.info; /* K index */ - OpCode op = cast(OpCode, opr + OP_ADDK); - finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINK, event); - } - else { /* 'e2' is neither an immediate nor a K operand */ - OpCode op = cast(OpCode, opr + OP_ADD); - if (flip) - swapexps(e1, e2); /* back to original order */ - codebinexpval(fs, op, e1, e2, line); /* use standard operators */ - } + if (tonumeral(e2, NULL) && luaK_exp2K(fs, e2)) /* K operand? */ + codebinK(fs, opr, e1, e2, flip, line); + else /* 'e2' is neither an immediate nor a K operand */ + codebinNoK(fs, opr, e1, e2, flip, line); } @@ -1471,35 +1518,27 @@ static void codecommutative (FuncState *fs, BinOpr op, flip = 1; } if (op == OPR_ADD && isSCint(e2)) /* immediate operand? */ - codebini(fs, cast(OpCode, OP_ADDI), e1, e2, flip, line, TM_ADD); + codebini(fs, OP_ADDI, e1, e2, flip, line, TM_ADD); else codearith(fs, op, e1, e2, flip, line); } /* -** Code bitwise operations; they are all associative, so the function +** Code bitwise operations; they are all commutative, so the function ** tries to put an integer constant as the 2nd operand (a K operand). */ static void codebitwise (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2, int line) { int flip = 0; - int v2; - OpCode op; - if (e1->k == VKINT && luaK_exp2RK(fs, e1)) { + if (e1->k == VKINT) { swapexps(e1, e2); /* 'e2' will be the constant operand */ flip = 1; } - else if (!(e2->k == VKINT && luaK_exp2RK(fs, e2))) { /* no constants? */ - op = cast(OpCode, opr + OP_ADD); - codebinexpval(fs, op, e1, e2, line); /* all-register opcodes */ - return; - } - v2 = e2->u.info; /* index in K array */ - op = cast(OpCode, opr + OP_ADDK); - lua_assert(ttisinteger(&fs->f->k[v2])); - finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINK, - cast(TMS, opr + TM_ADD)); + if (e2->k == VKINT && luaK_exp2K(fs, e2)) /* K operand? */ + codebinK(fs, opr, e1, e2, flip, line); + else /* no constants */ + codebinNoK(fs, opr, e1, e2, flip, line); } @@ -1507,25 +1546,27 @@ static void codebitwise (FuncState *fs, BinOpr opr, ** Emit code for order comparisons. When using an immediate operand, ** 'isfloat' tells whether the original value was a float. */ -static void codeorder (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2) { +static void codeorder (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) { int r1, r2; int im; int isfloat = 0; + OpCode op; if (isSCnumber(e2, &im, &isfloat)) { /* use immediate operand */ r1 = luaK_exp2anyreg(fs, e1); r2 = im; - op = cast(OpCode, (op - OP_LT) + OP_LTI); + op = binopr2op(opr, OPR_LT, OP_LTI); } else if (isSCnumber(e1, &im, &isfloat)) { /* transform (A < B) to (B > A) and (A <= B) to (B >= A) */ r1 = luaK_exp2anyreg(fs, e2); r2 = im; - op = (op == OP_LT) ? OP_GTI : OP_GEI; + op = binopr2op(opr, OPR_LT, OP_GTI); } else { /* regular case, compare two registers */ r1 = luaK_exp2anyreg(fs, e1); r2 = luaK_exp2anyreg(fs, e2); + op = binopr2op(opr, OPR_LT, OP_LT); } freeexps(fs, e1, e2); e1->u.info = condjump(fs, op, r1, r2, isfloat, 1); @@ -1551,7 +1592,7 @@ static void codeeq (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) { op = OP_EQI; r2 = im; /* immediate operand */ } - else if (luaK_exp2RK(fs, e2)) { /* 1st expression is constant? */ + else if (luaK_exp2RK(fs, e2)) { /* 2nd expression is constant? */ op = OP_EQK; r2 = e2->u.info; /* constant index */ } @@ -1568,16 +1609,16 @@ static void codeeq (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) { /* ** Apply prefix operation 'op' to expression 'e'. */ -void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e, int line) { +void luaK_prefix (FuncState *fs, UnOpr opr, expdesc *e, int line) { static const expdesc ef = {VKINT, {0}, NO_JUMP, NO_JUMP}; luaK_dischargevars(fs, e); - switch (op) { + switch (opr) { case OPR_MINUS: case OPR_BNOT: /* use 'ef' as fake 2nd operand */ - if (constfolding(fs, op + LUA_OPUNM, e, &ef)) + if (constfolding(fs, opr + LUA_OPUNM, e, &ef)) break; /* else */ /* FALLTHROUGH */ case OPR_LEN: - codeunexpval(fs, cast(OpCode, op + OP_UNM), e, line); + codeunexpval(fs, unopr2op(opr), e, line); break; case OPR_NOT: codenot(fs, e); break; default: lua_assert(0); @@ -1611,7 +1652,8 @@ void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) { case OPR_SHL: case OPR_SHR: { if (!tonumeral(v, NULL)) luaK_exp2anyreg(fs, v); - /* else keep numeral, which may be folded with 2nd operand */ + /* else keep numeral, which may be folded or used as an immediate + operand */ break; } case OPR_EQ: case OPR_NE: { @@ -1706,30 +1748,27 @@ void luaK_posfix (FuncState *fs, BinOpr opr, /* coded as (r1 >> -I) */; } else /* regular case (two registers) */ - codebinexpval(fs, OP_SHL, e1, e2, line); + codebinexpval(fs, opr, e1, e2, line); break; } case OPR_SHR: { if (isSCint(e2)) codebini(fs, OP_SHRI, e1, e2, 0, line, TM_SHR); /* r1 >> I */ else /* regular case (two registers) */ - codebinexpval(fs, OP_SHR, e1, e2, line); + codebinexpval(fs, opr, e1, e2, line); break; } case OPR_EQ: case OPR_NE: { codeeq(fs, opr, e1, e2); break; } - case OPR_LT: case OPR_LE: { - OpCode op = cast(OpCode, (opr - OPR_EQ) + OP_EQ); - codeorder(fs, op, e1, e2); - break; - } case OPR_GT: case OPR_GE: { /* '(a > b)' <=> '(b < a)'; '(a >= b)' <=> '(b <= a)' */ - OpCode op = cast(OpCode, (opr - OPR_NE) + OP_EQ); swapexps(e1, e2); - codeorder(fs, op, e1, e2); + opr = cast(BinOpr, (opr - OPR_GT) + OPR_LT); + } /* FALLTHROUGH */ + case OPR_LT: case OPR_LE: { + codeorder(fs, opr, e1, e2); break; } default: lua_assert(0); diff --git a/src/lua/lcorolib.c b/src/lua/lcorolib.c index 785a1e81a..40b880b14 100644 --- a/src/lua/lcorolib.c +++ b/src/lua/lcorolib.c @@ -76,7 +76,7 @@ static int luaB_auxwrap (lua_State *L) { if (l_unlikely(r < 0)) { /* error? */ int stat = lua_status(co); if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */ - stat = lua_resetthread(co); /* close its tbc variables */ + stat = lua_resetthread(co, L); /* close its tbc variables */ lua_assert(stat != LUA_OK); lua_xmove(co, L, 1); /* move error message to the caller */ } @@ -172,7 +172,7 @@ static int luaB_close (lua_State *L) { int status = auxstatus(L, co); switch (status) { case COS_DEAD: case COS_YIELD: { - status = lua_resetthread(co); + status = lua_resetthread(co, L); if (status == LUA_OK) { lua_pushboolean(L, 1); return 1; diff --git a/src/lua/ldebug.c b/src/lua/ldebug.c index a716d95e2..28b1caabf 100644 --- a/src/lua/ldebug.c +++ b/src/lua/ldebug.c @@ -182,10 +182,10 @@ static const char *upvalname (const Proto *p, int uv) { static const char *findvararg (CallInfo *ci, int n, StkId *pos) { - if (clLvalue(s2v(ci->func))->p->is_vararg) { + if (clLvalue(s2v(ci->func.p))->p->is_vararg) { int nextra = ci->u.l.nextraargs; if (n >= -nextra) { /* 'n' is negative */ - *pos = ci->func - nextra - (n + 1); + *pos = ci->func.p - nextra - (n + 1); return "(vararg)"; /* generic name for any vararg */ } } @@ -194,7 +194,7 @@ static const char *findvararg (CallInfo *ci, int n, StkId *pos) { const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, StkId *pos) { - StkId base = ci->func + 1; + StkId base = ci->func.p + 1; const char *name = NULL; if (isLua(ci)) { if (n < 0) /* access to vararg values? */ @@ -203,7 +203,7 @@ const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, StkId *pos) { name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci)); } if (name == NULL) { /* no 'standard' name? */ - StkId limit = (ci == L->ci) ? L->top : ci->next->func; + StkId limit = (ci == L->ci) ? L->top.p : ci->next->func.p; if (limit - base >= n && n > 0) { /* is 'n' inside 'ci' stack? */ /* generic name for any valid slot */ name = isLua(ci) ? "(temporary)" : "(C temporary)"; @@ -221,16 +221,16 @@ LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) { const char *name; lua_lock(L); if (ar == NULL) { /* information about non-active function? */ - if (!isLfunction(s2v(L->top - 1))) /* not a Lua function? */ + if (!isLfunction(s2v(L->top.p - 1))) /* not a Lua function? */ name = NULL; else /* consider live variables at function start (parameters) */ - name = luaF_getlocalname(clLvalue(s2v(L->top - 1))->p, n, 0); + name = luaF_getlocalname(clLvalue(s2v(L->top.p - 1))->p, n, 0); } else { /* active function; get information through 'ar' */ StkId pos = NULL; /* to avoid warnings */ name = luaG_findlocal(L, ar->i_ci, n, &pos); if (name) { - setobjs2s(L, L->top, pos); + setobjs2s(L, L->top.p, pos); api_incr_top(L); } } @@ -245,8 +245,8 @@ LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) { lua_lock(L); name = luaG_findlocal(L, ar->i_ci, n, &pos); if (name) { - setobjs2s(L, pos, L->top - 1); - L->top--; /* pop value */ + setobjs2s(L, pos, L->top.p - 1); + L->top.p--; /* pop value */ } lua_unlock(L); return name; @@ -289,7 +289,7 @@ static int nextline (const Proto *p, int currentline, int pc) { static void collectvalidlines (lua_State *L, Closure *f) { if (noLuaClosure(f)) { - setnilvalue(s2v(L->top)); + setnilvalue(s2v(L->top.p)); api_incr_top(L); } else { @@ -298,7 +298,7 @@ static void collectvalidlines (lua_State *L, Closure *f) { const Proto *p = f->l.p; int currentline = p->linedefined; Table *t = luaH_new(L); /* new table to store active lines */ - sethvalue2s(L, L->top, t); /* push it on stack */ + sethvalue2s(L, L->top.p, t); /* push it on stack */ api_incr_top(L); setbtvalue(&v); /* boolean 'true' to be the value of all indices */ if (!p->is_vararg) /* regular function? */ @@ -388,20 +388,20 @@ LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) { lua_lock(L); if (*what == '>') { ci = NULL; - func = s2v(L->top - 1); + func = s2v(L->top.p - 1); api_check(L, ttisfunction(func), "function expected"); what++; /* skip the '>' */ - L->top--; /* pop function */ + L->top.p--; /* pop function */ } else { ci = ar->i_ci; - func = s2v(ci->func); + func = s2v(ci->func.p); lua_assert(ttisfunction(func)); } cl = ttisclosure(func) ? clvalue(func) : NULL; status = auxgetinfo(L, what, ar, cl, ci); if (strchr(what, 'f')) { - setobj2s(L, L->top, func); + setobj2s(L, L->top.p, func); api_incr_top(L); } if (strchr(what, 'L')) @@ -656,18 +656,19 @@ static const char *funcnamefromcall (lua_State *L, CallInfo *ci, /* -** Check whether pointer 'o' points to some value in the stack -** frame of the current function. Because 'o' may not point to a -** value in this stack, we cannot compare it with the region -** boundaries (undefined behaviour in ISO C). +** Check whether pointer 'o' points to some value in the stack frame of +** the current function and, if so, returns its index. Because 'o' may +** not point to a value in this stack, we cannot compare it with the +** region boundaries (undefined behavior in ISO C). */ -static int isinstack (CallInfo *ci, const TValue *o) { - StkId pos; - for (pos = ci->func + 1; pos < ci->top; pos++) { - if (o == s2v(pos)) - return 1; +static int instack (CallInfo *ci, const TValue *o) { + int pos; + StkId base = ci->func.p + 1; + for (pos = 0; base + pos < ci->top.p; pos++) { + if (o == s2v(base + pos)) + return pos; } - return 0; /* not found */ + return -1; /* not found */ } @@ -681,7 +682,7 @@ static const char *getupvalname (CallInfo *ci, const TValue *o, LClosure *c = ci_func(ci); int i; for (i = 0; i < c->nupvalues; i++) { - if (c->upvals[i]->v == o) { + if (c->upvals[i]->v.p == o) { *name = upvalname(c->p, i); return "upvalue"; } @@ -708,9 +709,11 @@ static const char *varinfo (lua_State *L, const TValue *o) { const char *kind = NULL; if (isLua(ci)) { kind = getupvalname(ci, o, &name); /* check whether 'o' is an upvalue */ - if (!kind && isinstack(ci, o)) /* no? try a register */ - kind = getobjname(ci_func(ci)->p, currentpc(ci), - cast_int(cast(StkId, o) - (ci->func + 1)), &name); + if (!kind) { /* not an upvalue? */ + int reg = instack(ci, o); /* try a register */ + if (reg >= 0) /* is 'o' a register? */ + kind = getobjname(ci_func(ci)->p, currentpc(ci), reg, &name); + } } return formatvarinfo(L, kind, name); } @@ -807,10 +810,10 @@ l_noret luaG_errormsg (lua_State *L) { if (L->errfunc != 0) { /* is there an error handling function? */ StkId errfunc = restorestack(L, L->errfunc); lua_assert(ttisfunction(s2v(errfunc))); - setobjs2s(L, L->top, L->top - 1); /* move argument */ - setobjs2s(L, L->top - 1, errfunc); /* push function */ - L->top++; /* assume EXTRA_STACK */ - luaD_callnoyield(L, L->top - 2, 1); /* call it */ + setobjs2s(L, L->top.p, L->top.p - 1); /* move argument */ + setobjs2s(L, L->top.p - 1, errfunc); /* push function */ + L->top.p++; /* assume EXTRA_STACK */ + luaD_callnoyield(L, L->top.p - 2, 1); /* call it */ } luaD_throw(L, LUA_ERRRUN); } @@ -824,8 +827,11 @@ l_noret luaG_runerror (lua_State *L, const char *fmt, ...) { va_start(argp, fmt); msg = luaO_pushvfstring(L, fmt, argp); /* format message */ va_end(argp); - if (isLua(ci)) /* if Lua function, add source:line information */ + if (isLua(ci)) { /* if Lua function, add source:line information */ luaG_addinfo(L, msg, ci_func(ci)->p->source, getcurrentline(ci)); + setobjs2s(L, L->top.p - 2, L->top.p - 1); /* remove 'msg' */ + L->top.p--; + } luaG_errormsg(L); } @@ -842,7 +848,7 @@ static int changedline (const Proto *p, int oldpc, int newpc) { if (p->lineinfo == NULL) /* no debug information? */ return 0; if (newpc - oldpc < MAXIWTHABS / 2) { /* not too far apart? */ - int delta = 0; /* line diference */ + int delta = 0; /* line difference */ int pc = oldpc; for (;;) { int lineinfo = p->lineinfo[++pc]; @@ -869,7 +875,7 @@ static int changedline (const Proto *p, int oldpc, int newpc) { ** invalid; if so, use zero as a valid value. (A wrong but valid 'oldpc' ** at most causes an extra call to a line hook.) ** This function is not "Protected" when called, so it should correct -** 'L->top' before calling anything that can run the GC. +** 'L->top.p' before calling anything that can run the GC. */ int luaG_traceexec (lua_State *L, const Instruction *pc) { CallInfo *ci = L->ci; @@ -892,7 +898,7 @@ int luaG_traceexec (lua_State *L, const Instruction *pc) { return 1; /* do not call hook again (VM yielded, so it did not move) */ } if (!isIT(*(ci->u.l.savedpc - 1))) /* top not being used? */ - L->top = ci->top; /* correct top */ + L->top.p = ci->top.p; /* correct top */ if (counthook) luaD_hook(L, LUA_HOOKCOUNT, -1, 0, 0); /* call count hook */ if (mask & LUA_MASKLINE) { diff --git a/src/lua/ldebug.h b/src/lua/ldebug.h index 974960e99..2c3074c61 100644 --- a/src/lua/ldebug.h +++ b/src/lua/ldebug.h @@ -15,7 +15,7 @@ /* Active Lua function (given call info) */ -#define ci_func(ci) (clLvalue(s2v((ci)->func))) +#define ci_func(ci) (clLvalue(s2v((ci)->func.p))) #define resethookcount(L) (L->hookcount = L->basehookcount) diff --git a/src/lua/ldo.c b/src/lua/ldo.c index a48e35f9d..2a0017ca6 100644 --- a/src/lua/ldo.c +++ b/src/lua/ldo.c @@ -104,11 +104,11 @@ void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) { } default: { lua_assert(errorstatus(errcode)); /* real error */ - setobjs2s(L, oldtop, L->top - 1); /* error message on current top */ + setobjs2s(L, oldtop, L->top.p - 1); /* error message on current top */ break; } } - L->top = oldtop + 1; + L->top.p = oldtop + 1; } @@ -121,7 +121,7 @@ l_noret luaD_throw (lua_State *L, int errcode) { global_State *g = G(L); errcode = luaE_resetthread(L, errcode); /* close all upvalues */ if (g->mainthread->errorJmp) { /* main thread has a handler? */ - setobjs2s(L, g->mainthread->top++, L->top - 1); /* copy error obj. */ + setobjs2s(L, g->mainthread->top.p++, L->top.p - 1); /* copy error obj. */ luaD_throw(g->mainthread, errcode); /* re-throw in main thread */ } else { /* no handler at all; abort */ @@ -157,16 +157,38 @@ int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { ** Stack reallocation ** =================================================================== */ -static void correctstack (lua_State *L, StkId oldstack, StkId newstack) { + + +/* +** Change all pointers to the stack into offsets. +*/ +static void relstack (lua_State *L) { + CallInfo *ci; + UpVal *up; + L->top.offset = savestack(L, L->top.p); + L->tbclist.offset = savestack(L, L->tbclist.p); + for (up = L->openupval; up != NULL; up = up->u.open.next) + up->v.offset = savestack(L, uplevel(up)); + for (ci = L->ci; ci != NULL; ci = ci->previous) { + ci->top.offset = savestack(L, ci->top.p); + ci->func.offset = savestack(L, ci->func.p); + } +} + + +/* +** Change back all offsets into pointers. +*/ +static void correctstack (lua_State *L) { CallInfo *ci; UpVal *up; - L->top = (L->top - oldstack) + newstack; - L->tbclist = (L->tbclist - oldstack) + newstack; + L->top.p = restorestack(L, L->top.offset); + L->tbclist.p = restorestack(L, L->tbclist.offset); for (up = L->openupval; up != NULL; up = up->u.open.next) - up->v = s2v((uplevel(up) - oldstack) + newstack); + up->v.p = s2v(restorestack(L, up->v.offset)); for (ci = L->ci; ci != NULL; ci = ci->previous) { - ci->top = (ci->top - oldstack) + newstack; - ci->func = (ci->func - oldstack) + newstack; + ci->top.p = restorestack(L, ci->top.offset); + ci->func.p = restorestack(L, ci->func.offset); if (isLua(ci)) ci->u.l.trap = 1; /* signal to update 'trap' in 'luaV_execute' */ } @@ -176,44 +198,45 @@ static void correctstack (lua_State *L, StkId oldstack, StkId newstack) { /* some space for error handling */ #define ERRORSTACKSIZE (LUAI_MAXSTACK + 200) - /* -** Reallocate the stack to a new size, correcting all pointers into -** it. (There are pointers to a stack from its upvalues, from its list -** of call infos, plus a few individual pointers.) The reallocation is -** done in two steps (allocation + free) because the correction must be -** done while both addresses (the old stack and the new one) are valid. -** (In ISO C, any pointer use after the pointer has been deallocated is -** undefined behavior.) +** Reallocate the stack to a new size, correcting all pointers into it. +** In ISO C, any pointer use after the pointer has been deallocated is +** undefined behavior. So, before the reallocation, all pointers are +** changed to offsets, and after the reallocation they are changed back +** to pointers. As during the reallocation the pointers are invalid, the +** reallocation cannot run emergency collections. +** ** In case of allocation error, raise an error or return false according ** to 'raiseerror'. */ int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) { int oldsize = stacksize(L); int i; - StkId newstack = luaM_reallocvector(L, NULL, 0, - newsize + EXTRA_STACK, StackValue); + StkId newstack; + int oldgcstop = G(L)->gcstopem; lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE); + relstack(L); /* change pointers to offsets */ + G(L)->gcstopem = 1; /* stop emergency collection */ + newstack = luaM_reallocvector(L, L->stack.p, oldsize + EXTRA_STACK, + newsize + EXTRA_STACK, StackValue); + G(L)->gcstopem = oldgcstop; /* restore emergency collection */ if (l_unlikely(newstack == NULL)) { /* reallocation failed? */ + correctstack(L); /* change offsets back to pointers */ if (raiseerror) luaM_error(L); else return 0; /* do not raise an error */ } - /* number of elements to be copied to the new stack */ - i = ((oldsize <= newsize) ? oldsize : newsize) + EXTRA_STACK; - memcpy(newstack, L->stack, i * sizeof(StackValue)); - for (; i < newsize + EXTRA_STACK; i++) + L->stack.p = newstack; + correctstack(L); /* change offsets back to pointers */ + L->stack_last.p = L->stack.p + newsize; + for (i = oldsize + EXTRA_STACK; i < newsize + EXTRA_STACK; i++) setnilvalue(s2v(newstack + i)); /* erase new segment */ - correctstack(L, L->stack, newstack); - luaM_freearray(L, L->stack, oldsize + EXTRA_STACK); - L->stack = newstack; - L->stack_last = L->stack + newsize; return 1; } /* -** Try to grow the stack by at least 'n' elements. when 'raiseerror' +** Try to grow the stack by at least 'n' elements. When 'raiseerror' ** is true, raises any error; otherwise, return 0 in case of errors. */ int luaD_growstack (lua_State *L, int n, int raiseerror) { @@ -227,35 +250,38 @@ int luaD_growstack (lua_State *L, int n, int raiseerror) { luaD_throw(L, LUA_ERRERR); /* error inside message handler */ return 0; /* if not 'raiseerror', just signal it */ } - else { + else if (n < LUAI_MAXSTACK) { /* avoids arithmetic overflows */ int newsize = 2 * size; /* tentative new size */ - int needed = cast_int(L->top - L->stack) + n; + int needed = cast_int(L->top.p - L->stack.p) + n; if (newsize > LUAI_MAXSTACK) /* cannot cross the limit */ newsize = LUAI_MAXSTACK; if (newsize < needed) /* but must respect what was asked for */ newsize = needed; if (l_likely(newsize <= LUAI_MAXSTACK)) return luaD_reallocstack(L, newsize, raiseerror); - else { /* stack overflow */ - /* add extra size to be able to handle the error message */ - luaD_reallocstack(L, ERRORSTACKSIZE, raiseerror); - if (raiseerror) - luaG_runerror(L, "stack overflow"); - return 0; - } } + /* else stack overflow */ + /* add extra size to be able to handle the error message */ + luaD_reallocstack(L, ERRORSTACKSIZE, raiseerror); + if (raiseerror) + luaG_runerror(L, "stack overflow"); + return 0; } +/* +** Compute how much of the stack is being used, by computing the +** maximum top of all call frames in the stack and the current top. +*/ static int stackinuse (lua_State *L) { CallInfo *ci; int res; - StkId lim = L->top; + StkId lim = L->top.p; for (ci = L->ci; ci != NULL; ci = ci->previous) { - if (lim < ci->top) lim = ci->top; + if (lim < ci->top.p) lim = ci->top.p; } - lua_assert(lim <= L->stack_last); - res = cast_int(lim - L->stack) + 1; /* part of stack in use */ + lua_assert(lim <= L->stack_last.p + EXTRA_STACK); + res = cast_int(lim - L->stack.p) + 1; /* part of stack in use */ if (res < LUA_MINSTACK) res = LUA_MINSTACK; /* ensure a minimum size */ return res; @@ -273,17 +299,13 @@ static int stackinuse (lua_State *L) { */ void luaD_shrinkstack (lua_State *L) { int inuse = stackinuse(L); - int nsize = inuse * 2; /* proposed new size */ - int max = inuse * 3; /* maximum "reasonable" size */ - if (max > LUAI_MAXSTACK) { - max = LUAI_MAXSTACK; /* respect stack limit */ - if (nsize > LUAI_MAXSTACK) - nsize = LUAI_MAXSTACK; - } + int max = (inuse > LUAI_MAXSTACK / 3) ? LUAI_MAXSTACK : inuse * 3; /* if thread is currently not handling a stack overflow and its size is larger than maximum "reasonable" size, shrink it */ - if (inuse <= LUAI_MAXSTACK && stacksize(L) > max) + if (inuse <= LUAI_MAXSTACK && stacksize(L) > max) { + int nsize = (inuse > LUAI_MAXSTACK / 2) ? LUAI_MAXSTACK : inuse * 2; luaD_reallocstack(L, nsize, 0); /* ok if that fails */ + } else /* don't change stack */ condmovestack(L,{},{}); /* (change only for debugging) */ luaE_shrinkCI(L); /* shrink CI list */ @@ -292,7 +314,7 @@ void luaD_shrinkstack (lua_State *L) { void luaD_inctop (lua_State *L) { luaD_checkstack(L, 1); - L->top++; + L->top.p++; } /* }================================================================== */ @@ -309,8 +331,8 @@ void luaD_hook (lua_State *L, int event, int line, if (hook && L->allowhook) { /* make sure there is a hook */ int mask = CIST_HOOKED; CallInfo *ci = L->ci; - ptrdiff_t top = savestack(L, L->top); /* preserve original 'top' */ - ptrdiff_t ci_top = savestack(L, ci->top); /* idem for 'ci->top' */ + ptrdiff_t top = savestack(L, L->top.p); /* preserve original 'top' */ + ptrdiff_t ci_top = savestack(L, ci->top.p); /* idem for 'ci->top' */ lua_Debug ar; ar.event = event; ar.currentline = line; @@ -320,11 +342,11 @@ void luaD_hook (lua_State *L, int event, int line, ci->u2.transferinfo.ftransfer = ftransfer; ci->u2.transferinfo.ntransfer = ntransfer; } - if (isLua(ci) && L->top < ci->top) - L->top = ci->top; /* protect entire activation register */ + if (isLua(ci) && L->top.p < ci->top.p) + L->top.p = ci->top.p; /* protect entire activation register */ luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */ - if (ci->top < L->top + LUA_MINSTACK) - ci->top = L->top + LUA_MINSTACK; + if (ci->top.p < L->top.p + LUA_MINSTACK) + ci->top.p = L->top.p + LUA_MINSTACK; L->allowhook = 0; /* cannot call hooks inside a hook */ ci->callstatus |= mask; lua_unlock(L); @@ -332,8 +354,8 @@ void luaD_hook (lua_State *L, int event, int line, lua_lock(L); lua_assert(!L->allowhook); L->allowhook = 1; - ci->top = restorestack(L, ci_top); - L->top = restorestack(L, top); + ci->top.p = restorestack(L, ci_top); + L->top.p = restorestack(L, top); ci->callstatus &= ~mask; } } @@ -364,7 +386,7 @@ void luaD_hookcall (lua_State *L, CallInfo *ci) { */ static void rethook (lua_State *L, CallInfo *ci, int nres) { if (L->hookmask & LUA_MASKRET) { /* is return hook on? */ - StkId firstres = L->top - nres; /* index of first result */ + StkId firstres = L->top.p - nres; /* index of first result */ int delta = 0; /* correction for vararg functions */ int ftransfer; if (isLua(ci)) { @@ -372,10 +394,10 @@ static void rethook (lua_State *L, CallInfo *ci, int nres) { if (p->is_vararg) delta = ci->u.l.nextraargs + p->numparams + 1; } - ci->func += delta; /* if vararg, back to virtual 'func' */ - ftransfer = cast(unsigned short, firstres - ci->func); + ci->func.p += delta; /* if vararg, back to virtual 'func' */ + ftransfer = cast(unsigned short, firstres - ci->func.p); luaD_hook(L, LUA_HOOKRET, -1, ftransfer, nres); /* call it */ - ci->func -= delta; + ci->func.p -= delta; } if (isLua(ci = ci->previous)) L->oldpc = pcRel(ci->u.l.savedpc, ci_func(ci)->p); /* set 'oldpc' */ @@ -394,9 +416,9 @@ StkId luaD_tryfuncTM (lua_State *L, StkId func) { tm = luaT_gettmbyobj(L, s2v(func), TM_CALL); /* (after previous GC) */ if (l_unlikely(ttisnil(tm))) luaG_callerror(L, s2v(func)); /* nothing to call */ - for (p = L->top; p > func; p--) /* open space for metamethod */ + for (p = L->top.p; p > func; p--) /* open space for metamethod */ setobjs2s(L, p, p-1); - L->top++; /* stack space pre-allocated by the caller */ + L->top.p++; /* stack space pre-allocated by the caller */ setobj2s(L, func, tm); /* metamethod is the new function to be called */ return func; } @@ -413,28 +435,29 @@ l_sinline void moveresults (lua_State *L, StkId res, int nres, int wanted) { int i; switch (wanted) { /* handle typical cases separately */ case 0: /* no values needed */ - L->top = res; + L->top.p = res; return; case 1: /* one value needed */ if (nres == 0) /* no results? */ setnilvalue(s2v(res)); /* adjust with nil */ else /* at least one result */ - setobjs2s(L, res, L->top - nres); /* move it to proper place */ - L->top = res + 1; + setobjs2s(L, res, L->top.p - nres); /* move it to proper place */ + L->top.p = res + 1; return; case LUA_MULTRET: wanted = nres; /* we want all results */ break; default: /* two/more results and/or to-be-closed variables */ if (hastocloseCfunc(wanted)) { /* to-be-closed variables? */ - ptrdiff_t savedres = savestack(L, res); L->ci->callstatus |= CIST_CLSRET; /* in case of yields */ L->ci->u2.nres = nres; - luaF_close(L, res, CLOSEKTOP, 1); + res = luaF_close(L, res, CLOSEKTOP, 1); L->ci->callstatus &= ~CIST_CLSRET; - if (L->hookmask) /* if needed, call hook after '__close's */ + if (L->hookmask) { /* if needed, call hook after '__close's */ + ptrdiff_t savedres = savestack(L, res); rethook(L, L->ci, nres); - res = restorestack(L, savedres); /* close and hook can move stack */ + res = restorestack(L, savedres); /* hook can move stack */ + } wanted = decodeNresults(wanted); if (wanted == LUA_MULTRET) wanted = nres; /* we want all results */ @@ -442,14 +465,14 @@ l_sinline void moveresults (lua_State *L, StkId res, int nres, int wanted) { break; } /* generic case */ - firstresult = L->top - nres; /* index of first result */ + firstresult = L->top.p - nres; /* index of first result */ if (nres > wanted) /* extra results? */ nres = wanted; /* don't need them */ for (i = 0; i < nres; i++) /* move all results to correct place */ setobjs2s(L, res + i, firstresult + i); for (; i < wanted; i++) /* complete wanted number of results */ setnilvalue(s2v(res + i)); - L->top = res + wanted; /* top points after the last result */ + L->top.p = res + wanted; /* top points after the last result */ } @@ -464,7 +487,7 @@ void luaD_poscall (lua_State *L, CallInfo *ci, int nres) { if (l_unlikely(L->hookmask && !hastocloseCfunc(wanted))) rethook(L, ci, nres); /* move results to proper place */ - moveresults(L, ci->func, nres, wanted); + moveresults(L, ci->func.p, nres, wanted); /* function cannot be in any of these cases when returning */ lua_assert(!(ci->callstatus & (CIST_HOOKED | CIST_YPCALL | CIST_FIN | CIST_TRAN | CIST_CLSRET))); @@ -479,10 +502,10 @@ void luaD_poscall (lua_State *L, CallInfo *ci, int nres) { l_sinline CallInfo *prepCallInfo (lua_State *L, StkId func, int nret, int mask, StkId top) { CallInfo *ci = L->ci = next_ci(L); /* new frame */ - ci->func = func; + ci->func.p = func; ci->nresults = nret; ci->callstatus = mask; - ci->top = top; + ci->top.p = top; return ci; } @@ -496,10 +519,10 @@ l_sinline int precallC (lua_State *L, StkId func, int nresults, CallInfo *ci; checkstackGCp(L, LUA_MINSTACK, func); /* ensure minimum stack size */ L->ci = ci = prepCallInfo(L, func, nresults, CIST_C, - L->top + LUA_MINSTACK); - lua_assert(ci->top <= L->stack_last); + L->top.p + LUA_MINSTACK); + lua_assert(ci->top.p <= L->stack_last.p); if (l_unlikely(L->hookmask & LUA_MASKCALL)) { - int narg = cast_int(L->top - func) - 1; + int narg = cast_int(L->top.p - func) - 1; luaD_hook(L, LUA_HOOKCALL, -1, 1, narg); } lua_unlock(L); @@ -531,17 +554,17 @@ int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int nfixparams = p->numparams; int i; checkstackGCp(L, fsize - delta, func); - ci->func -= delta; /* restore 'func' (if vararg) */ + ci->func.p -= delta; /* restore 'func' (if vararg) */ for (i = 0; i < narg1; i++) /* move down function and arguments */ - setobjs2s(L, ci->func + i, func + i); - func = ci->func; /* moved-down function */ + setobjs2s(L, ci->func.p + i, func + i); + func = ci->func.p; /* moved-down function */ for (; narg1 <= nfixparams; narg1++) setnilvalue(s2v(func + narg1)); /* complete missing arguments */ - ci->top = func + 1 + fsize; /* top for new function */ - lua_assert(ci->top <= L->stack_last); + ci->top.p = func + 1 + fsize; /* top for new function */ + lua_assert(ci->top.p <= L->stack_last.p); ci->u.l.savedpc = p->code; /* starting point */ ci->callstatus |= CIST_TAIL; - L->top = func + narg1; /* set top */ + L->top.p = func + narg1; /* set top */ return -1; } default: { /* not a function */ @@ -574,15 +597,15 @@ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) { case LUA_VLCL: { /* Lua function */ CallInfo *ci; Proto *p = clLvalue(s2v(func))->p; - int narg = cast_int(L->top - func) - 1; /* number of real arguments */ + int narg = cast_int(L->top.p - func) - 1; /* number of real arguments */ int nfixparams = p->numparams; int fsize = p->maxstacksize; /* frame size */ checkstackGCp(L, fsize, func); L->ci = ci = prepCallInfo(L, func, nresults, 0, func + 1 + fsize); ci->u.l.savedpc = p->code; /* starting point */ for (; narg < nfixparams; narg++) - setnilvalue(s2v(L->top++)); /* complete missing arguments */ - lua_assert(ci->top <= L->stack_last); + setnilvalue(s2v(L->top.p++)); /* complete missing arguments */ + lua_assert(ci->top.p <= L->stack_last.p); return ci; } default: { /* not a function */ @@ -598,12 +621,17 @@ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) { ** Call a function (C or Lua) through C. 'inc' can be 1 (increment ** number of recursive invocations in the C stack) or nyci (the same ** plus increment number of non-yieldable calls). +** This function can be called with some use of EXTRA_STACK, so it should +** check the stack before doing anything else. 'luaD_precall' already +** does that. */ -l_sinline void ccall (lua_State *L, StkId func, int nResults, int inc) { +l_sinline void ccall (lua_State *L, StkId func, int nResults, l_uint32 inc) { CallInfo *ci; L->nCcalls += inc; - if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS)) + if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS)) { + checkstackp(L, 0, func); /* free any use of EXTRA_STACK */ luaE_checkcstack(L); + } if ((ci = luaD_precall(L, func, nResults)) != NULL) { /* Lua function? */ ci->callstatus = CIST_FRESH; /* mark that it is a "fresh" execute */ luaV_execute(L, ci); /* call it */ @@ -651,8 +679,7 @@ static int finishpcallk (lua_State *L, CallInfo *ci) { else { /* error */ StkId func = restorestack(L, ci->u2.funcidx); L->allowhook = getoah(ci->callstatus); /* restore 'allowhook' */ - luaF_close(L, func, status, 1); /* can yield or raise an error */ - func = restorestack(L, ci->u2.funcidx); /* stack may be moved */ + func = luaF_close(L, func, status, 1); /* can yield or raise an error */ luaD_seterrorobj(L, status, func); luaD_shrinkstack(L); /* restore stack size in case of overflow */ setcistrecst(ci, LUA_OK); /* clear original status */ @@ -740,8 +767,8 @@ static CallInfo *findpcall (lua_State *L) { ** coroutine error handler and should not kill the coroutine.) */ static int resume_error (lua_State *L, const char *msg, int narg) { - L->top -= narg; /* remove args from the stack */ - setsvalue2s(L, L->top, luaS_new(L, msg)); /* push error message */ + L->top.p -= narg; /* remove args from the stack */ + setsvalue2s(L, L->top.p, luaS_new(L, msg)); /* push error message */ api_incr_top(L); lua_unlock(L); return LUA_ERRRUN; @@ -757,7 +784,7 @@ static int resume_error (lua_State *L, const char *msg, int narg) { */ static void resume (lua_State *L, void *ud) { int n = *(cast(int*, ud)); /* number of arguments */ - StkId firstArg = L->top - n; /* first argument */ + StkId firstArg = L->top.p - n; /* first argument */ CallInfo *ci = L->ci; if (L->status == LUA_OK) /* starting a coroutine? */ ccall(L, firstArg - 1, LUA_MULTRET, 0); /* just call its body */ @@ -765,7 +792,7 @@ static void resume (lua_State *L, void *ud) { lua_assert(L->status == LUA_YIELD); L->status = LUA_OK; /* mark that it is running (again) */ if (isLua(ci)) { /* yielded inside a hook? */ - L->top = firstArg; /* discard arguments */ + L->top.p = firstArg; /* discard arguments */ luaV_execute(L, ci); /* just continue running Lua code */ } else { /* 'common' yield */ @@ -808,7 +835,7 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs, if (L->status == LUA_OK) { /* may be starting a coroutine */ if (L->ci != &L->base_ci) /* not in base level? */ return resume_error(L, "cannot resume non-suspended coroutine", nargs); - else if (L->top - (L->ci->func + 1) == nargs) /* no function? */ + else if (L->top.p - (L->ci->func.p + 1) == nargs) /* no function? */ return resume_error(L, "cannot resume dead coroutine", nargs); } else if (L->status != LUA_YIELD) /* ended with errors? */ @@ -826,11 +853,11 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs, lua_assert(status == L->status); /* normal end or yield */ else { /* unrecoverable error */ L->status = cast_byte(status); /* mark thread as 'dead' */ - luaD_seterrorobj(L, status, L->top); /* push error message */ - L->ci->top = L->top; + luaD_seterrorobj(L, status, L->top.p); /* push error message */ + L->ci->top.p = L->top.p; } *nresults = (status == LUA_YIELD) ? L->ci->u2.nyield - : cast_int(L->top - (L->ci->func + 1)); + : cast_int(L->top.p - (L->ci->func.p + 1)); lua_unlock(L); return status; } @@ -985,7 +1012,7 @@ int luaD_protectedparser (lua_State *L, ZIO *z, const char *name, p.dyd.gt.arr = NULL; p.dyd.gt.size = 0; p.dyd.label.arr = NULL; p.dyd.label.size = 0; luaZ_initbuffer(L, &p.buff); - status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc); + status = luaD_pcall(L, f_parser, &p, savestack(L, L->top.p), L->errfunc); luaZ_freebuffer(L, &p.buff); luaM_freearray(L, p.dyd.actvar.arr, p.dyd.actvar.size); luaM_freearray(L, p.dyd.gt.arr, p.dyd.gt.size); diff --git a/src/lua/ldo.h b/src/lua/ldo.h index 911e67f66..1aa446ad0 100644 --- a/src/lua/ldo.h +++ b/src/lua/ldo.h @@ -8,6 +8,7 @@ #define ldo_h +#include "llimits.h" #include "lobject.h" #include "lstate.h" #include "lzio.h" @@ -23,7 +24,7 @@ ** at every check. */ #define luaD_checkstackaux(L,n,pre,pos) \ - if (l_unlikely(L->stack_last - L->top <= (n))) \ + if (l_unlikely(L->stack_last.p - L->top.p <= (n))) \ { pre; luaD_growstack(L, n, 1); pos; } \ else { condmovestack(L,pre,pos); } @@ -32,11 +33,18 @@ -#define savestack(L,p) ((char *)(p) - (char *)L->stack) -#define restorestack(L,n) ((StkId)((char *)L->stack + (n))) +#define savestack(L,pt) (cast_charp(pt) - cast_charp(L->stack.p)) +#define restorestack(L,n) cast(StkId, cast_charp(L->stack.p) + (n)) /* macro to check stack size, preserving 'p' */ +#define checkstackp(L,n,p) \ + luaD_checkstackaux(L, n, \ + ptrdiff_t t__ = savestack(L, p), /* save 'p' */ \ + p = restorestack(L, t__)) /* 'pos' part: restore 'p' */ + + +/* macro to check stack size and GC, preserving 'p' */ #define checkstackGCp(L,n,p) \ luaD_checkstackaux(L, n, \ ptrdiff_t t__ = savestack(L, p); /* save 'p' */ \ @@ -58,7 +66,8 @@ LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name, LUAI_FUNC void luaD_hook (lua_State *L, int event, int line, int fTransfer, int nTransfer); LUAI_FUNC void luaD_hookcall (lua_State *L, CallInfo *ci); -LUAI_FUNC int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int narg1, int delta); +LUAI_FUNC int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, + int narg1, int delta); LUAI_FUNC CallInfo *luaD_precall (lua_State *L, StkId func, int nResults); LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults); LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults); diff --git a/src/lua/ldump.c b/src/lua/ldump.c index f848b669c..f231691b7 100644 --- a/src/lua/ldump.c +++ b/src/lua/ldump.c @@ -10,6 +10,7 @@ #include "lprefix.h" +#include #include #include "lua.h" @@ -55,8 +56,11 @@ static void dumpByte (DumpState *D, int y) { } -/* dumpInt Buff Size */ -#define DIBS ((sizeof(size_t) * 8 / 7) + 1) +/* +** 'dumpSize' buffer size: each byte can store up to 7 bits. (The "+6" +** rounds up the division.) +*/ +#define DIBS ((sizeof(size_t) * CHAR_BIT + 6) / 7) static void dumpSize (DumpState *D, size_t x) { lu_byte buff[DIBS]; diff --git a/src/lua/lfunc.c b/src/lua/lfunc.c index f5889a21d..0945f241d 100644 --- a/src/lua/lfunc.c +++ b/src/lua/lfunc.c @@ -50,8 +50,8 @@ void luaF_initupvals (lua_State *L, LClosure *cl) { for (i = 0; i < cl->nupvalues; i++) { GCObject *o = luaC_newobj(L, LUA_VUPVAL, sizeof(UpVal)); UpVal *uv = gco2upv(o); - uv->v = &uv->u.value; /* make it closed */ - setnilvalue(uv->v); + uv->v.p = &uv->u.value; /* make it closed */ + setnilvalue(uv->v.p); cl->upvals[i] = uv; luaC_objbarrier(L, cl, uv); } @@ -62,12 +62,11 @@ void luaF_initupvals (lua_State *L, LClosure *cl) { ** Create a new upvalue at the given level, and link it to the list of ** open upvalues of 'L' after entry 'prev'. **/ -static UpVal *newupval (lua_State *L, int tbc, StkId level, UpVal **prev) { +static UpVal *newupval (lua_State *L, StkId level, UpVal **prev) { GCObject *o = luaC_newobj(L, LUA_VUPVAL, sizeof(UpVal)); UpVal *uv = gco2upv(o); UpVal *next = *prev; - uv->v = s2v(level); /* current value lives in the stack */ - uv->tbc = tbc; + uv->v.p = s2v(level); /* current value lives in the stack */ uv->u.open.next = next; /* link it to list of open upvalues */ uv->u.open.previous = prev; if (next) @@ -96,7 +95,7 @@ UpVal *luaF_findupval (lua_State *L, StkId level) { pp = &p->u.open.next; } /* not found: create a new upvalue after 'pp' */ - return newupval(L, 0, level, pp); + return newupval(L, level, pp); } @@ -106,12 +105,12 @@ UpVal *luaF_findupval (lua_State *L, StkId level) { ** (This function assumes EXTRA_STACK.) */ static void callclosemethod (lua_State *L, TValue *obj, TValue *err, int yy) { - StkId top = L->top; + StkId top = L->top.p; const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE); setobj2s(L, top, tm); /* will call metamethod... */ setobj2s(L, top + 1, obj); /* with 'self' as the 1st argument */ setobj2s(L, top + 2, err); /* and error msg. as 2nd argument */ - L->top = top + 3; /* add function and arguments */ + L->top.p = top + 3; /* add function and arguments */ if (yy) luaD_call(L, top, 0); else @@ -126,7 +125,7 @@ static void callclosemethod (lua_State *L, TValue *obj, TValue *err, int yy) { static void checkclosemth (lua_State *L, StkId level) { const TValue *tm = luaT_gettmbyobj(L, s2v(level), TM_CLOSE); if (ttisnil(tm)) { /* no metamethod? */ - int idx = cast_int(level - L->ci->func); /* variable index */ + int idx = cast_int(level - L->ci->func.p); /* variable index */ const char *vname = luaG_findlocal(L, L->ci, idx, NULL); if (vname == NULL) vname = "?"; luaG_runerror(L, "variable '%s' got a non-closable value", vname); @@ -160,23 +159,23 @@ static void prepcallclosemth (lua_State *L, StkId level, int status, int yy) { ** is used.) */ #define MAXDELTA \ - ((256ul << ((sizeof(L->stack->tbclist.delta) - 1) * 8)) - 1) + ((256ul << ((sizeof(L->stack.p->tbclist.delta) - 1) * 8)) - 1) /* ** Insert a variable in the list of to-be-closed variables. */ void luaF_newtbcupval (lua_State *L, StkId level) { - lua_assert(level > L->tbclist); + lua_assert(level > L->tbclist.p); if (l_isfalse(s2v(level))) return; /* false doesn't need to be closed */ checkclosemth(L, level); /* value must have a close method */ - while (cast_uint(level - L->tbclist) > MAXDELTA) { - L->tbclist += MAXDELTA; /* create a dummy node at maximum delta */ - L->tbclist->tbclist.delta = 0; + while (cast_uint(level - L->tbclist.p) > MAXDELTA) { + L->tbclist.p += MAXDELTA; /* create a dummy node at maximum delta */ + L->tbclist.p->tbclist.delta = 0; } - level->tbclist.delta = cast(unsigned short, level - L->tbclist); - L->tbclist = level; + level->tbclist.delta = cast(unsigned short, level - L->tbclist.p); + L->tbclist.p = level; } @@ -196,10 +195,10 @@ void luaF_closeupval (lua_State *L, StkId level) { StkId upl; /* stack index pointed by 'uv' */ while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) { TValue *slot = &uv->u.value; /* new position for value */ - lua_assert(uplevel(uv) < L->top); + lua_assert(uplevel(uv) < L->top.p); luaF_unlinkupval(uv); /* remove upvalue from 'openupval' list */ - setobj(L, slot, uv->v); /* move value to upvalue slot */ - uv->v = slot; /* now current value lives here */ + setobj(L, slot, uv->v.p); /* move value to upvalue slot */ + uv->v.p = slot; /* now current value lives here */ if (!iswhite(uv)) { /* neither white nor dead? */ nw2black(uv); /* closed upvalues cannot be gray */ luaC_barrier(L, uv, slot); @@ -209,31 +208,32 @@ void luaF_closeupval (lua_State *L, StkId level) { /* -** Remove firt element from the tbclist plus its dummy nodes. +** Remove first element from the tbclist plus its dummy nodes. */ static void poptbclist (lua_State *L) { - StkId tbc = L->tbclist; + StkId tbc = L->tbclist.p; lua_assert(tbc->tbclist.delta > 0); /* first element cannot be dummy */ tbc -= tbc->tbclist.delta; - while (tbc > L->stack && tbc->tbclist.delta == 0) + while (tbc > L->stack.p && tbc->tbclist.delta == 0) tbc -= MAXDELTA; /* remove dummy nodes */ - L->tbclist = tbc; + L->tbclist.p = tbc; } /* ** Close all upvalues and to-be-closed variables up to the given stack -** level. +** level. Return restored 'level'. */ -void luaF_close (lua_State *L, StkId level, int status, int yy) { +StkId luaF_close (lua_State *L, StkId level, int status, int yy) { ptrdiff_t levelrel = savestack(L, level); luaF_closeupval(L, level); /* first, close the upvalues */ - while (L->tbclist >= level) { /* traverse tbc's down to that level */ - StkId tbc = L->tbclist; /* get variable index */ + while (L->tbclist.p >= level) { /* traverse tbc's down to that level */ + StkId tbc = L->tbclist.p; /* get variable index */ poptbclist(L); /* remove it from list */ prepcallclosemth(L, tbc, status, yy); /* close variable */ level = restorestack(L, levelrel); } + return level; } diff --git a/src/lua/lfunc.h b/src/lua/lfunc.h index dc1cebccd..3be265efb 100644 --- a/src/lua/lfunc.h +++ b/src/lua/lfunc.h @@ -29,10 +29,10 @@ #define MAXUPVAL 255 -#define upisopen(up) ((up)->v != &(up)->u.value) +#define upisopen(up) ((up)->v.p != &(up)->u.value) -#define uplevel(up) check_exp(upisopen(up), cast(StkId, (up)->v)) +#define uplevel(up) check_exp(upisopen(up), cast(StkId, (up)->v.p)) /* @@ -54,7 +54,7 @@ LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl); LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level); LUAI_FUNC void luaF_newtbcupval (lua_State *L, StkId level); LUAI_FUNC void luaF_closeupval (lua_State *L, StkId level); -LUAI_FUNC void luaF_close (lua_State *L, StkId level, int status, int yy); +LUAI_FUNC StkId luaF_close (lua_State *L, StkId level, int status, int yy); LUAI_FUNC void luaF_unlinkupval (UpVal *uv); LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f); LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number, diff --git a/src/lua/lgc.c b/src/lua/lgc.c index 42a73d813..a3094ff57 100644 --- a/src/lua/lgc.c +++ b/src/lua/lgc.c @@ -252,12 +252,13 @@ void luaC_fix (lua_State *L, GCObject *o) { /* -** create a new collectable object (with given type and size) and link -** it to 'allgc' list. +** create a new collectable object (with given type, size, and offset) +** and link it to 'allgc' list. */ -GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) { +GCObject *luaC_newobjdt (lua_State *L, int tt, size_t sz, size_t offset) { global_State *g = G(L); - GCObject *o = cast(GCObject *, luaM_newobject(L, novariant(tt), sz)); + char *p = cast_charp(luaM_newobject(L, novariant(tt), sz)); + GCObject *o = cast(GCObject *, p + offset); o->marked = luaC_white(g); o->tt = tt; o->next = g->allgc; @@ -265,6 +266,11 @@ GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) { return o; } + +GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) { + return luaC_newobjdt(L, tt, sz, 0); +} + /* }====================================================== */ @@ -301,7 +307,7 @@ static void reallymarkobject (global_State *g, GCObject *o) { set2gray(uv); /* open upvalues are kept gray */ else set2black(uv); /* closed upvalues are visited here */ - markvalue(g, uv->v); /* mark its content */ + markvalue(g, uv->v.p); /* mark its content */ break; } case LUA_VUSERDATA: { @@ -376,7 +382,7 @@ static int remarkupvals (global_State *g) { work++; if (!iswhite(uv)) { /* upvalue already visited? */ lua_assert(upisopen(uv) && isgray(uv)); - markvalue(g, uv->v); /* mark its value */ + markvalue(g, uv->v.p); /* mark its value */ } } } @@ -620,19 +626,19 @@ static int traverseLclosure (global_State *g, LClosure *cl) { */ static int traversethread (global_State *g, lua_State *th) { UpVal *uv; - StkId o = th->stack; + StkId o = th->stack.p; if (isold(th) || g->gcstate == GCSpropagate) linkgclist(th, g->grayagain); /* insert into 'grayagain' list */ if (o == NULL) return 1; /* stack not completely built yet */ lua_assert(g->gcstate == GCSatomic || th->openupval == NULL || isintwups(th)); - for (; o < th->top; o++) /* mark live elements in the stack */ + for (; o < th->top.p; o++) /* mark live elements in the stack */ markvalue(g, s2v(o)); for (uv = th->openupval; uv != NULL; uv = uv->u.open.next) markobject(g, uv); /* open upvalues cannot be collected */ if (g->gcstate == GCSatomic) { /* final traversal? */ - for (; o < th->stack_last + EXTRA_STACK; o++) + for (; o < th->stack_last.p + EXTRA_STACK; o++) setnilvalue(s2v(o)); /* clear dead stack slice */ /* 'remarkupvals' may have removed thread from 'twups' list */ if (!isintwups(th) && th->openupval != NULL) { @@ -892,7 +898,7 @@ static GCObject *udata2finalize (global_State *g) { static void dothecall (lua_State *L, void *ud) { UNUSED(ud); - luaD_callnoyield(L, L->top - 2, 0); + luaD_callnoyield(L, L->top.p - 2, 0); } @@ -909,16 +915,16 @@ static void GCTM (lua_State *L) { int oldgcstp = g->gcstp; g->gcstp |= GCSTPGC; /* avoid GC steps */ L->allowhook = 0; /* stop debug hooks during GC metamethod */ - setobj2s(L, L->top++, tm); /* push finalizer... */ - setobj2s(L, L->top++, &v); /* ... and its argument */ + setobj2s(L, L->top.p++, tm); /* push finalizer... */ + setobj2s(L, L->top.p++, &v); /* ... and its argument */ L->ci->callstatus |= CIST_FIN; /* will run a finalizer */ - status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top - 2), 0); + status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top.p - 2), 0); L->ci->callstatus &= ~CIST_FIN; /* not running a finalizer anymore */ L->allowhook = oldah; /* restore hooks */ g->gcstp = oldgcstp; /* restore state */ if (l_unlikely(status != LUA_OK)) { /* error while running __gc? */ luaE_warnerror(L, "__gc"); - L->top--; /* pops error object */ + L->top.p--; /* pops error object */ } } } @@ -1041,7 +1047,25 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) { ** ======================================================= */ -static void setpause (global_State *g); + +/* +** Set the "time" to wait before starting a new GC cycle; cycle will +** start when memory use hits the threshold of ('estimate' * pause / +** PAUSEADJ). (Division by 'estimate' should be OK: it cannot be zero, +** because Lua cannot even start with less than PAUSEADJ bytes). +*/ +static void setpause (global_State *g) { + l_mem threshold, debt; + int pause = getgcparam(g->gcpause); + l_mem estimate = g->GCestimate / PAUSEADJ; /* adjust 'estimate' */ + lua_assert(estimate > 0); + threshold = (pause < MAX_LMEM / estimate) /* overflow? */ + ? estimate * pause /* no overflow */ + : MAX_LMEM; /* overflow; truncate to maximum */ + debt = gettotalbytes(g) - threshold; + if (debt > 0) debt = 0; + luaE_setdebt(g, debt); +} /* @@ -1285,6 +1309,15 @@ static void atomic2gen (lua_State *L, global_State *g) { } +/* +** Set debt for the next minor collection, which will happen when +** memory grows 'genminormul'%. +*/ +static void setminordebt (global_State *g) { + luaE_setdebt(g, -(cast(l_mem, (gettotalbytes(g) / 100)) * g->genminormul)); +} + + /* ** Enter generational mode. Must go until the end of an atomic cycle ** to ensure that all objects are correctly marked and weak tables @@ -1297,6 +1330,7 @@ static lu_mem entergen (lua_State *L, global_State *g) { luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ numobjs = atomic(L); /* propagates all and then do the atomic stuff */ atomic2gen(L, g); + setminordebt(g); /* set debt assuming next cycle will be minor */ return numobjs; } @@ -1342,15 +1376,6 @@ static lu_mem fullgen (lua_State *L, global_State *g) { } -/* -** Set debt for the next minor collection, which will happen when -** memory grows 'genminormul'%. -*/ -static void setminordebt (global_State *g) { - luaE_setdebt(g, -(cast(l_mem, (gettotalbytes(g) / 100)) * g->genminormul)); -} - - /* ** Does a major collection after last collection was a "bad collection". ** @@ -1422,8 +1447,8 @@ static void genstep (lua_State *L, global_State *g) { lu_mem numobjs = fullgen(L, g); /* do a major collection */ if (gettotalbytes(g) < majorbase + (majorinc / 2)) { /* collected at least half of memory growth since last major - collection; keep doing minor collections */ - setminordebt(g); + collection; keep doing minor collections. */ + lua_assert(g->lastatomic == 0); } else { /* bad collection */ g->lastatomic = numobjs; /* signal that last collection was bad */ @@ -1449,26 +1474,6 @@ static void genstep (lua_State *L, global_State *g) { */ -/* -** Set the "time" to wait before starting a new GC cycle; cycle will -** start when memory use hits the threshold of ('estimate' * pause / -** PAUSEADJ). (Division by 'estimate' should be OK: it cannot be zero, -** because Lua cannot even start with less than PAUSEADJ bytes). -*/ -static void setpause (global_State *g) { - l_mem threshold, debt; - int pause = getgcparam(g->gcpause); - l_mem estimate = g->GCestimate / PAUSEADJ; /* adjust 'estimate' */ - lua_assert(estimate > 0); - threshold = (pause < MAX_LMEM / estimate) /* overflow? */ - ? estimate * pause /* no overflow */ - : MAX_LMEM; /* overflow; truncate to maximum */ - debt = gettotalbytes(g) - threshold; - if (debt > 0) debt = 0; - luaE_setdebt(g, debt); -} - - /* ** Enter first sweep phase. ** The call to 'sweeptolive' makes the pointer point to an object @@ -1676,12 +1681,15 @@ static void incstep (lua_State *L, global_State *g) { } /* -** performs a basic GC step if collector is running +** Performs a basic GC step if collector is running. (If collector is +** not running, set a reasonable debt to avoid it being called at +** every single check.) */ void luaC_step (lua_State *L) { global_State *g = G(L); - lua_assert(!g->gcemergency); - if (gcrunning(g)) { /* running? */ + if (!gcrunning(g)) /* not running? */ + luaE_setdebt(g, -2000); + else { if(isdecGCmodegen(g)) genstep(L, g); else diff --git a/src/lua/lgc.h b/src/lua/lgc.h index 4a125634b..538f6edcc 100644 --- a/src/lua/lgc.h +++ b/src/lua/lgc.h @@ -172,24 +172,27 @@ #define luaC_checkGC(L) luaC_condGC(L,(void)0,(void)0) -#define luaC_barrier(L,p,v) ( \ - (iscollectable(v) && isblack(p) && iswhite(gcvalue(v))) ? \ - luaC_barrier_(L,obj2gco(p),gcvalue(v)) : cast_void(0)) - -#define luaC_barrierback(L,p,v) ( \ - (iscollectable(v) && isblack(p) && iswhite(gcvalue(v))) ? \ - luaC_barrierback_(L,p) : cast_void(0)) - #define luaC_objbarrier(L,p,o) ( \ (isblack(p) && iswhite(o)) ? \ luaC_barrier_(L,obj2gco(p),obj2gco(o)) : cast_void(0)) +#define luaC_barrier(L,p,v) ( \ + iscollectable(v) ? luaC_objbarrier(L,p,gcvalue(v)) : cast_void(0)) + +#define luaC_objbarrierback(L,p,o) ( \ + (isblack(p) && iswhite(o)) ? luaC_barrierback_(L,p) : cast_void(0)) + +#define luaC_barrierback(L,p,v) ( \ + iscollectable(v) ? luaC_objbarrierback(L, p, gcvalue(v)) : cast_void(0)) + LUAI_FUNC void luaC_fix (lua_State *L, GCObject *o); LUAI_FUNC void luaC_freeallobjects (lua_State *L); LUAI_FUNC void luaC_step (lua_State *L); LUAI_FUNC void luaC_runtilstate (lua_State *L, int statesmask); LUAI_FUNC void luaC_fullgc (lua_State *L, int isemergency); LUAI_FUNC GCObject *luaC_newobj (lua_State *L, int tt, size_t sz); +LUAI_FUNC GCObject *luaC_newobjdt (lua_State *L, int tt, size_t sz, + size_t offset); LUAI_FUNC void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v); LUAI_FUNC void luaC_barrierback_ (lua_State *L, GCObject *o); LUAI_FUNC void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt); diff --git a/src/lua/linit.c b/src/lua/linit.c index 9a5bcfdcc..69808f84f 100644 --- a/src/lua/linit.c +++ b/src/lua/linit.c @@ -50,9 +50,6 @@ static const luaL_Reg loadedlibs[] = { {LUA_MATHLIBNAME, luaopen_math}, {LUA_UTF8LIBNAME, luaopen_utf8}, {LUA_DBLIBNAME, luaopen_debug}, - /****** Pi-hole modification ******/ - {LUA_PIHOLELIBNAME, luaopen_pihole}, - /**********************************/ {NULL, NULL} }; diff --git a/src/lua/llex.c b/src/lua/llex.c index e99151787..5fc39a5cd 100644 --- a/src/lua/llex.c +++ b/src/lua/llex.c @@ -128,7 +128,7 @@ l_noret luaX_syntaxerror (LexState *ls, const char *msg) { ** ensuring there is only one copy of each unique string. The table ** here is used as a set: the string enters as the key, while its value ** is irrelevant. We use the string itself as the value only because it -** is a TValue readly available. Later, the code generation can change +** is a TValue readily available. Later, the code generation can change ** this value. */ TString *luaX_newstring (LexState *ls, const char *str, size_t l) { @@ -138,12 +138,12 @@ TString *luaX_newstring (LexState *ls, const char *str, size_t l) { if (!ttisnil(o)) /* string already present? */ ts = keystrval(nodefromval(o)); /* get saved copy */ else { /* not in use yet */ - TValue *stv = s2v(L->top++); /* reserve stack space for string */ + TValue *stv = s2v(L->top.p++); /* reserve stack space for string */ setsvalue(L, stv, ts); /* temporarily anchor the string */ luaH_finishset(L, ls->h, stv, o, stv); /* t[string] = string */ /* table is not a metatable, so it does not need to invalidate cache */ luaC_checkGC(L); - L->top--; /* remove string from stack */ + L->top.p--; /* remove string from stack */ } return ts; } diff --git a/src/lua/llimits.h b/src/lua/llimits.h index 52a32f92e..1c826f7be 100644 --- a/src/lua/llimits.h +++ b/src/lua/llimits.h @@ -71,11 +71,24 @@ typedef signed char ls_byte; /* -** conversion of pointer to unsigned integer: -** this is for hashing only; there is no problem if the integer -** cannot hold the whole pointer value +** conversion of pointer to unsigned integer: this is for hashing only; +** there is no problem if the integer cannot hold the whole pointer +** value. (In strict ISO C this may cause undefined behavior, but no +** actual machine seems to bother.) */ -#define point2uint(p) ((unsigned int)((size_t)(p) & UINT_MAX)) +#if !defined(LUA_USE_C89) && defined(__STDC_VERSION__) && \ + __STDC_VERSION__ >= 199901L +#include +#if defined(UINTPTR_MAX) /* even in C99 this type is optional */ +#define L_P2I uintptr_t +#else /* no 'intptr'? */ +#define L_P2I uintmax_t /* use the largest available integer */ +#endif +#else /* C89 option */ +#define L_P2I size_t +#endif + +#define point2uint(p) ((unsigned int)((L_P2I)(p) & UINT_MAX)) diff --git a/src/lua/lmathlib.c b/src/lua/lmathlib.c index e0c61a168..d0b1e1e5d 100644 --- a/src/lua/lmathlib.c +++ b/src/lua/lmathlib.c @@ -267,7 +267,7 @@ static int math_type (lua_State *L) { /* try to find an integer type with at least 64 bits */ -#if (ULONG_MAX >> 31 >> 31) >= 3 +#if ((ULONG_MAX >> 31) >> 31) >= 3 /* 'long' has at least 64 bits */ #define Rand64 unsigned long @@ -277,9 +277,9 @@ static int math_type (lua_State *L) { /* there is a 'long long' type (which must have at least 64 bits) */ #define Rand64 unsigned long long -#elif (LUA_MAXUNSIGNED >> 31 >> 31) >= 3 +#elif ((LUA_MAXUNSIGNED >> 31) >> 31) >= 3 -/* 'lua_Integer' has at least 64 bits */ +/* 'lua_Unsigned' has at least 64 bits */ #define Rand64 lua_Unsigned #endif @@ -500,12 +500,12 @@ static lua_Number I2d (Rand64 x) { /* convert a 'Rand64' to a 'lua_Unsigned' */ static lua_Unsigned I2UInt (Rand64 x) { - return ((lua_Unsigned)trim32(x.h) << 31 << 1) | (lua_Unsigned)trim32(x.l); + return (((lua_Unsigned)trim32(x.h) << 31) << 1) | (lua_Unsigned)trim32(x.l); } /* convert a 'lua_Unsigned' to a 'Rand64' */ static Rand64 Int2I (lua_Unsigned n) { - return packI((lu_int32)(n >> 31 >> 1), (lu_int32)n); + return packI((lu_int32)((n >> 31) >> 1), (lu_int32)n); } #endif /* } */ diff --git a/src/lua/lmem.c b/src/lua/lmem.c index 9029d588c..9800a86fc 100644 --- a/src/lua/lmem.c +++ b/src/lua/lmem.c @@ -22,25 +22,6 @@ #include "lstate.h" -#if defined(EMERGENCYGCTESTS) -/* -** First allocation will fail whenever not building initial state. -** (This fail will trigger 'tryagain' and a full GC cycle at every -** allocation.) -*/ -static void *firsttry (global_State *g, void *block, size_t os, size_t ns) { - if (completestate(g) && ns > 0) /* frees never fail */ - return NULL; /* fail */ - else /* normal allocation */ - return (*g->frealloc)(g->ud, block, os, ns); -} -#else -#define firsttry(g,block,os,ns) ((*g->frealloc)(g->ud, block, os, ns)) -#endif - - - - /* ** About the realloc function: @@ -60,6 +41,43 @@ static void *firsttry (global_State *g, void *block, size_t os, size_t ns) { */ +/* +** Macro to call the allocation function. +*/ +#define callfrealloc(g,block,os,ns) ((*g->frealloc)(g->ud, block, os, ns)) + + +/* +** When an allocation fails, it will try again after an emergency +** collection, except when it cannot run a collection. The GC should +** not be called while the state is not fully built, as the collector +** is not yet fully initialized. Also, it should not be called when +** 'gcstopem' is true, because then the interpreter is in the middle of +** a collection step. +*/ +#define cantryagain(g) (completestate(g) && !g->gcstopem) + + + + +#if defined(EMERGENCYGCTESTS) +/* +** First allocation will fail except when freeing a block (frees never +** fail) and when it cannot try again; this fail will trigger 'tryagain' +** and a full GC cycle at every allocation. +*/ +static void *firsttry (global_State *g, void *block, size_t os, size_t ns) { + if (ns > 0 && cantryagain(g)) + return NULL; /* fail */ + else /* normal allocation */ + return callfrealloc(g, block, os, ns); +} +#else +#define firsttry(g,block,os,ns) callfrealloc(g, block, os, ns) +#endif + + + /* @@ -132,7 +150,7 @@ l_noret luaM_toobig (lua_State *L) { void luaM_free_ (lua_State *L, void *block, size_t osize) { global_State *g = G(L); lua_assert((osize == 0) == (block == NULL)); - (*g->frealloc)(g->ud, block, osize, 0); + callfrealloc(g, block, osize, 0); g->GCdebt -= osize; } @@ -140,19 +158,15 @@ void luaM_free_ (lua_State *L, void *block, size_t osize) { /* ** In case of allocation fail, this function will do an emergency ** collection to free some memory and then try the allocation again. -** The GC should not be called while state is not fully built, as the -** collector is not yet fully initialized. Also, it should not be called -** when 'gcstopem' is true, because then the interpreter is in the -** middle of a collection step. */ static void *tryagain (lua_State *L, void *block, size_t osize, size_t nsize) { global_State *g = G(L); - if (completestate(g) && !g->gcstopem) { + if (cantryagain(g)) { luaC_fullgc(L, 1); /* try to free some memory... */ - return (*g->frealloc)(g->ud, block, osize, nsize); /* try again */ + return callfrealloc(g, block, osize, nsize); /* try again */ } - else return NULL; /* cannot free any memory without a full state */ + else return NULL; /* cannot run an emergency collection */ } diff --git a/src/lua/loadlib.c b/src/lua/loadlib.c index 6f9fa3736..d792dffaa 100644 --- a/src/lua/loadlib.c +++ b/src/lua/loadlib.c @@ -708,8 +708,13 @@ static const luaL_Reg ll_funcs[] = { static void createsearcherstable (lua_State *L) { - static const lua_CFunction searchers[] = - {searcher_preload, searcher_Lua, searcher_C, searcher_Croot, NULL}; + static const lua_CFunction searchers[] = { + searcher_preload, + searcher_Lua, + searcher_C, + searcher_Croot, + NULL + }; int i; /* create 'searchers' table */ lua_createtable(L, sizeof(searchers)/sizeof(searchers[0]) - 1, 0); diff --git a/src/lua/lobject.c b/src/lua/lobject.c index 301aa900b..f73ffc6d9 100644 --- a/src/lua/lobject.c +++ b/src/lua/lobject.c @@ -62,7 +62,7 @@ static lua_Integer intarith (lua_State *L, int op, lua_Integer v1, case LUA_OPBOR: return intop(|, v1, v2); case LUA_OPBXOR: return intop(^, v1, v2); case LUA_OPSHL: return luaV_shiftl(v1, v2); - case LUA_OPSHR: return luaV_shiftl(v1, -v2); + case LUA_OPSHR: return luaV_shiftr(v1, v2); case LUA_OPUNM: return intop(-, 0, v1); case LUA_OPBNOT: return intop(^, ~l_castS2U(0), v1); default: lua_assert(0); return 0; @@ -386,29 +386,39 @@ void luaO_tostring (lua_State *L, TValue *obj) { ** =================================================================== */ -/* size for buffer space used by 'luaO_pushvfstring' */ -#define BUFVFS 200 +/* +** Size for buffer space used by 'luaO_pushvfstring'. It should be +** (LUA_IDSIZE + MAXNUMBER2STR) + a minimal space for basic messages, +** so that 'luaG_addinfo' can work directly on the buffer. +*/ +#define BUFVFS (LUA_IDSIZE + MAXNUMBER2STR + 95) /* buffer used by 'luaO_pushvfstring' */ typedef struct BuffFS { lua_State *L; - int pushed; /* number of string pieces already on the stack */ + int pushed; /* true if there is a part of the result on the stack */ int blen; /* length of partial string in 'space' */ char space[BUFVFS]; /* holds last part of the result */ } BuffFS; /* -** Push given string to the stack, as part of the buffer, and -** join the partial strings in the stack into one. +** Push given string to the stack, as part of the result, and +** join it to previous partial result if there is one. +** It may call 'luaV_concat' while using one slot from EXTRA_STACK. +** This call cannot invoke metamethods, as both operands must be +** strings. It can, however, raise an error if the result is too +** long. In that case, 'luaV_concat' frees the extra slot before +** raising the error. */ -static void pushstr (BuffFS *buff, const char *str, size_t l) { +static void pushstr (BuffFS *buff, const char *str, size_t lstr) { lua_State *L = buff->L; - setsvalue2s(L, L->top, luaS_newlstr(L, str, l)); - L->top++; /* may use one extra slot */ - buff->pushed++; - luaV_concat(L, buff->pushed); /* join partial results into one */ - buff->pushed = 1; + setsvalue2s(L, L->top.p, luaS_newlstr(L, str, lstr)); + L->top.p++; /* may use one slot from EXTRA_STACK */ + if (!buff->pushed) /* no previous string on the stack? */ + buff->pushed = 1; /* now there is one */ + else /* join previous string with new one */ + luaV_concat(L, 2); } @@ -454,7 +464,7 @@ static void addstr2buff (BuffFS *buff, const char *str, size_t slen) { /* -** Add a number to the buffer. +** Add a numeral to the buffer. */ static void addnum2buff (BuffFS *buff, TValue *num) { char *numbuff = getbuff(buff, MAXNUMBER2STR); @@ -532,7 +542,7 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { addstr2buff(&buff, fmt, strlen(fmt)); /* rest of 'fmt' */ clearbuff(&buff); /* empty buffer into the stack */ lua_assert(buff.pushed == 1); - return svalue(s2v(L->top - 1)); + return svalue(s2v(L->top.p - 1)); } diff --git a/src/lua/lobject.h b/src/lua/lobject.h index 0e05b3e42..556608e4a 100644 --- a/src/lua/lobject.h +++ b/src/lua/lobject.h @@ -52,6 +52,8 @@ typedef union Value { lua_CFunction f; /* light C functions */ lua_Integer i; /* integer numbers */ lua_Number n; /* float numbers */ + /* not used, but may avoid warnings for uninitialized value */ + lu_byte ub; } Value; @@ -155,6 +157,17 @@ typedef union StackValue { /* index to stack elements */ typedef StackValue *StkId; + +/* +** When reallocating the stack, change all pointers to the stack into +** proper offsets. +*/ +typedef union { + StkId p; /* actual pointer */ + ptrdiff_t offset; /* used while the stack is being reallocated */ +} StkIdRel; + + /* convert a 'StackValue' to a 'TValue' */ #define s2v(o) (&(o)->val) @@ -615,8 +628,10 @@ typedef struct Proto { */ typedef struct UpVal { CommonHeader; - lu_byte tbc; /* true if it represents a to-be-closed variable */ - TValue *v; /* points to stack or to its own value */ + union { + TValue *p; /* points to stack or to its own value */ + ptrdiff_t offset; /* used while the stack is being reallocated */ + } v; union { struct { /* (when open) */ struct UpVal *next; /* linked list */ diff --git a/src/lua/lopcodes.h b/src/lua/lopcodes.h index 7c2745159..4c5514539 100644 --- a/src/lua/lopcodes.h +++ b/src/lua/lopcodes.h @@ -21,7 +21,7 @@ iABC C(8) | B(8) |k| A(8) | Op(7) | iABx Bx(17) | A(8) | Op(7) | iAsBx sBx (signed)(17) | A(8) | Op(7) | iAx Ax(25) | Op(7) | -isJ sJ(25) | Op(7) | +isJ sJ (signed)(25) | Op(7) | A signed argument is represented in excess K: the represented value is the written unsigned value minus K, where K is half the maximum for the diff --git a/src/lua/loslib.c b/src/lua/loslib.c index 3e20d622b..ad5a92768 100644 --- a/src/lua/loslib.c +++ b/src/lua/loslib.c @@ -30,23 +30,14 @@ */ #if !defined(LUA_STRFTIMEOPTIONS) /* { */ -/* options for ANSI C 89 (only 1-char options) */ -#define L_STRFTIMEC89 "aAbBcdHIjmMpSUwWxXyYZ%" - -/* options for ISO C 99 and POSIX */ -#define L_STRFTIMEC99 "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \ - "||" "EcECExEXEyEY" "OdOeOHOIOmOMOSOuOUOVOwOWOy" /* two-char options */ - -/* options for Windows */ -#define L_STRFTIMEWIN "aAbBcdHIjmMpSUwWxXyYzZ%" \ - "||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */ - #if defined(LUA_USE_WINDOWS) -#define LUA_STRFTIMEOPTIONS L_STRFTIMEWIN -#elif defined(LUA_USE_C89) -#define LUA_STRFTIMEOPTIONS L_STRFTIMEC89 +#define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYzZ%" \ + "||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */ +#elif defined(LUA_USE_C89) /* ANSI C 89 (only 1-char options) */ +#define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYZ%" #else /* C99 specification */ -#define LUA_STRFTIMEOPTIONS L_STRFTIMEC99 +#define LUA_STRFTIMEOPTIONS "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \ + "||" "EcECExEXEyEY" "OdOeOHOIOmOMOSOuOUOVOwOWOy" /* two-char options */ #endif #endif /* } */ @@ -138,12 +129,21 @@ /* }================================================================== */ +#if !defined(l_system) +#if defined(LUA_USE_IOS) +/* Despite claiming to be ISO C, iOS does not implement 'system'. */ +#define l_system(cmd) ((cmd) == NULL ? 0 : -1) +#else +#define l_system(cmd) system(cmd) /* default definition */ +#endif +#endif + static int os_execute (lua_State *L) { const char *cmd = luaL_optstring(L, 1, NULL); int stat; errno = 0; - stat = system(cmd); + stat = l_system(cmd); if (cmd != NULL) return luaL_execresult(L, stat); else { @@ -260,9 +260,7 @@ static int getfield (lua_State *L, const char *key, int d, int delta) { res = d; } else { - /* unsigned avoids overflow when lua_Integer has 32 bits */ - if (!(res >= 0 ? (lua_Unsigned)res <= (lua_Unsigned)INT_MAX + delta - : (lua_Integer)INT_MIN + delta <= res)) + if (!(res >= 0 ? res - delta <= INT_MAX : INT_MIN + delta <= res)) return luaL_error(L, "field '%s' is out-of-bound", key); res -= delta; } diff --git a/src/lua/lparser.c b/src/lua/lparser.c index 3abe3d751..b745f236f 100644 --- a/src/lua/lparser.c +++ b/src/lua/lparser.c @@ -468,6 +468,7 @@ static void singlevar (LexState *ls, expdesc *var) { expdesc key; singlevaraux(fs, ls->envn, var, 1); /* get environment variable */ lua_assert(var->k != VVOID); /* this one must exist */ + luaK_exp2anyregup(fs, var); /* but could be a constant */ codestring(&key, varname); /* key is variable name */ luaK_indexed(fs, var, &key); /* env[varname] */ } @@ -520,12 +521,12 @@ static l_noret jumpscopeerror (LexState *ls, Labeldesc *gt) { /* ** Solves the goto at index 'g' to given 'label' and removes it -** from the list of pending goto's. +** from the list of pending gotos. ** If it jumps into the scope of some variable, raises an error. */ static void solvegoto (LexState *ls, int g, Labeldesc *label) { int i; - Labellist *gl = &ls->dyd->gt; /* list of goto's */ + Labellist *gl = &ls->dyd->gt; /* list of gotos */ Labeldesc *gt = &gl->arr[g]; /* goto to be resolved */ lua_assert(eqstr(gt->name, label->name)); if (l_unlikely(gt->nactvar < label->nactvar)) /* enter some scope? */ @@ -579,7 +580,7 @@ static int newgotoentry (LexState *ls, TString *name, int line, int pc) { /* ** Solves forward jumps. Check whether new label 'lb' matches any ** pending gotos in current block and solves them. Return true -** if any of the goto's need to close upvalues. +** if any of the gotos need to close upvalues. */ static int solvegotos (LexState *ls, Labeldesc *lb) { Labellist *gl = &ls->dyd->gt; @@ -600,7 +601,7 @@ static int solvegotos (LexState *ls, Labeldesc *lb) { /* ** Create a new label with the given 'name' at the given 'line'. ** 'last' tells whether label is the last non-op statement in its -** block. Solves all pending goto's to this new label and adds +** block. Solves all pending gotos to this new label and adds ** a close instruction if necessary. ** Returns true iff it added a close instruction. */ @@ -673,19 +674,19 @@ static void leaveblock (FuncState *fs) { LexState *ls = fs->ls; int hasclose = 0; int stklevel = reglevel(fs, bl->nactvar); /* level outside the block */ - if (bl->isloop) /* fix pending breaks? */ + removevars(fs, bl->nactvar); /* remove block locals */ + lua_assert(bl->nactvar == fs->nactvar); /* back to level on entry */ + if (bl->isloop) /* has to fix pending breaks? */ hasclose = createlabel(ls, luaS_newliteral(ls->L, "break"), 0, 0); - if (!hasclose && bl->previous && bl->upval) + if (!hasclose && bl->previous && bl->upval) /* still need a 'close'? */ luaK_codeABC(fs, OP_CLOSE, stklevel, 0, 0); - fs->bl = bl->previous; - removevars(fs, bl->nactvar); - lua_assert(bl->nactvar == fs->nactvar); fs->freereg = stklevel; /* free registers */ ls->dyd->label.n = bl->firstlabel; /* remove local labels */ - if (bl->previous) /* inner block? */ - movegotosout(fs, bl); /* update pending gotos to outer block */ + fs->bl = bl->previous; /* current block now is previous one */ + if (bl->previous) /* was it a nested block? */ + movegotosout(fs, bl); /* update pending gotos to enclosing block */ else { - if (bl->firstgoto < ls->dyd->gt.n) /* pending gotos in outer block? */ + if (bl->firstgoto < ls->dyd->gt.n) /* still pending gotos? */ undefgoto(ls, &ls->dyd->gt.arr[bl->firstgoto]); /* error */ } } @@ -1943,10 +1944,10 @@ LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, LexState lexstate; FuncState funcstate; LClosure *cl = luaF_newLclosure(L, 1); /* create main closure */ - setclLvalue2s(L, L->top, cl); /* anchor it (to avoid being collected) */ + setclLvalue2s(L, L->top.p, cl); /* anchor it (to avoid being collected) */ luaD_inctop(L); lexstate.h = luaH_new(L); /* create table for scanner */ - sethvalue2s(L, L->top, lexstate.h); /* anchor it */ + sethvalue2s(L, L->top.p, lexstate.h); /* anchor it */ luaD_inctop(L); funcstate.f = cl->p = luaF_newproto(L); luaC_objbarrier(L, cl, cl->p); @@ -1960,7 +1961,7 @@ LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, lua_assert(!funcstate.prev && funcstate.nups == 1 && !lexstate.fs); /* all scopes should be correctly finished */ lua_assert(dyd->actvar.n == 0 && dyd->gt.n == 0 && dyd->label.n == 0); - L->top--; /* remove scanner's table */ + L->top.p--; /* remove scanner's table */ return cl; /* closure is on the stack, too */ } diff --git a/src/lua/lstate.c b/src/lua/lstate.c index 1ffe1a0f7..1fbefb4b1 100644 --- a/src/lua/lstate.c +++ b/src/lua/lstate.c @@ -180,33 +180,33 @@ LUAI_FUNC void luaE_incCstack (lua_State *L) { static void stack_init (lua_State *L1, lua_State *L) { int i; CallInfo *ci; /* initialize stack array */ - L1->stack = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue); - L1->tbclist = L1->stack; + L1->stack.p = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue); + L1->tbclist.p = L1->stack.p; for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++) - setnilvalue(s2v(L1->stack + i)); /* erase new stack */ - L1->top = L1->stack; - L1->stack_last = L1->stack + BASIC_STACK_SIZE; + setnilvalue(s2v(L1->stack.p + i)); /* erase new stack */ + L1->top.p = L1->stack.p; + L1->stack_last.p = L1->stack.p + BASIC_STACK_SIZE; /* initialize first ci */ ci = &L1->base_ci; ci->next = ci->previous = NULL; ci->callstatus = CIST_C; - ci->func = L1->top; + ci->func.p = L1->top.p; ci->u.c.k = NULL; ci->nresults = 0; - setnilvalue(s2v(L1->top)); /* 'function' entry for this 'ci' */ - L1->top++; - ci->top = L1->top + LUA_MINSTACK; + setnilvalue(s2v(L1->top.p)); /* 'function' entry for this 'ci' */ + L1->top.p++; + ci->top.p = L1->top.p + LUA_MINSTACK; L1->ci = ci; } static void freestack (lua_State *L) { - if (L->stack == NULL) + if (L->stack.p == NULL) return; /* stack not completely built yet */ L->ci = &L->base_ci; /* free the entire 'ci' list */ luaE_freeCI(L); lua_assert(L->nci == 0); - luaM_freearray(L, L->stack, stacksize(L) + EXTRA_STACK); /* free stack */ + luaM_freearray(L, L->stack.p, stacksize(L) + EXTRA_STACK); /* free stack */ } @@ -248,7 +248,7 @@ static void f_luaopen (lua_State *L, void *ud) { */ static void preinit_thread (lua_State *L, global_State *g) { G(L) = g; - L->stack = NULL; + L->stack.p = NULL; L->ci = NULL; L->nci = 0; L->twups = L; /* thread has no upvalues */ @@ -284,20 +284,16 @@ static void close_state (lua_State *L) { LUA_API lua_State *lua_newthread (lua_State *L) { - global_State *g; + global_State *g = G(L); + GCObject *o; lua_State *L1; lua_lock(L); - g = G(L); luaC_checkGC(L); /* create new thread */ - L1 = &cast(LX *, luaM_newobject(L, LUA_TTHREAD, sizeof(LX)))->l; - L1->marked = luaC_white(g); - L1->tt = LUA_VTHREAD; - /* link it on list 'allgc' */ - L1->next = g->allgc; - g->allgc = obj2gco(L1); + o = luaC_newobjdt(L, LUA_TTHREAD, sizeof(LX), offsetof(LX, l)); + L1 = gco2th(o); /* anchor it on L stack */ - setthvalue2s(L, L->top, L1); + setthvalue2s(L, L->top.p, L1); api_incr_top(L); preinit_thread(L1, g); L1->hookmask = L->hookmask; @@ -316,7 +312,7 @@ LUA_API lua_State *lua_newthread (lua_State *L) { void luaE_freethread (lua_State *L, lua_State *L1) { LX *l = fromstate(L1); - luaF_closeupval(L1, L1->stack); /* close all upvalues */ + luaF_closeupval(L1, L1->stack.p); /* close all upvalues */ lua_assert(L1->openupval == NULL); luai_userstatefree(L, L1); freestack(L1); @@ -326,26 +322,27 @@ void luaE_freethread (lua_State *L, lua_State *L1) { int luaE_resetthread (lua_State *L, int status) { CallInfo *ci = L->ci = &L->base_ci; /* unwind CallInfo list */ - setnilvalue(s2v(L->stack)); /* 'function' entry for basic 'ci' */ - ci->func = L->stack; + setnilvalue(s2v(L->stack.p)); /* 'function' entry for basic 'ci' */ + ci->func.p = L->stack.p; ci->callstatus = CIST_C; if (status == LUA_YIELD) status = LUA_OK; L->status = LUA_OK; /* so it can run __close metamethods */ status = luaD_closeprotected(L, 1, status); if (status != LUA_OK) /* errors? */ - luaD_seterrorobj(L, status, L->stack + 1); + luaD_seterrorobj(L, status, L->stack.p + 1); else - L->top = L->stack + 1; - ci->top = L->top + LUA_MINSTACK; - luaD_reallocstack(L, cast_int(ci->top - L->stack), 0); + L->top.p = L->stack.p + 1; + ci->top.p = L->top.p + LUA_MINSTACK; + luaD_reallocstack(L, cast_int(ci->top.p - L->stack.p), 0); return status; } -LUA_API int lua_resetthread (lua_State *L) { +LUA_API int lua_resetthread (lua_State *L, lua_State *from) { int status; lua_lock(L); + L->nCcalls = (from) ? getCcalls(from) : 0; status = luaE_resetthread(L, L->status); lua_unlock(L); return status; @@ -426,7 +423,7 @@ void luaE_warning (lua_State *L, const char *msg, int tocont) { ** Generate a warning from an error message */ void luaE_warnerror (lua_State *L, const char *where) { - TValue *errobj = s2v(L->top - 1); /* error object */ + TValue *errobj = s2v(L->top.p - 1); /* error object */ const char *msg = (ttisstring(errobj)) ? svalue(errobj) : "error object is not a string"; diff --git a/src/lua/lstate.h b/src/lua/lstate.h index 61e82cde7..8bf6600e3 100644 --- a/src/lua/lstate.h +++ b/src/lua/lstate.h @@ -9,6 +9,11 @@ #include "lua.h" + +/* Some header files included here need this definition */ +typedef struct CallInfo CallInfo; + + #include "lobject.h" #include "ltm.h" #include "lzio.h" @@ -139,7 +144,7 @@ struct lua_longjmp; /* defined in ldo.c */ #define BASIC_STACK_SIZE (2*LUA_MINSTACK) -#define stacksize(th) cast_int((th)->stack_last - (th)->stack) +#define stacksize(th) cast_int((th)->stack_last.p - (th)->stack.p) /* kinds of Garbage Collection */ @@ -169,9 +174,9 @@ typedef struct stringtable { ** - field 'transferinfo' is used only during call/returnhooks, ** before the function starts or after it ends. */ -typedef struct CallInfo { - StkId func; /* function index in the stack */ - StkId top; /* top for this function */ +struct CallInfo { + StkIdRel func; /* function index in the stack */ + StkIdRel top; /* top for this function */ struct CallInfo *previous, *next; /* dynamic call link */ union { struct { /* only for Lua functions */ @@ -196,7 +201,7 @@ typedef struct CallInfo { } u2; short nresults; /* expected number of results from this function */ unsigned short callstatus; -} CallInfo; +}; /* @@ -291,7 +296,7 @@ typedef struct global_State { struct lua_State *mainthread; TString *memerrmsg; /* message for memory-allocation errors */ TString *tmname[TM_N]; /* array with tag-method names */ - struct Table *mt[LUA_NUMTAGS]; /* metatables for basic types */ + struct Table *mt[LUA_NUMTYPES]; /* metatables for basic types */ TString *strcache[STRCACHE_N][STRCACHE_M]; /* cache for strings in API */ lua_WarnFunction warnf; /* warning function */ void *ud_warn; /* auxiliary data to 'warnf' */ @@ -306,13 +311,13 @@ struct lua_State { lu_byte status; lu_byte allowhook; unsigned short nci; /* number of items in 'ci' list */ - StkId top; /* first free slot in the stack */ + StkIdRel top; /* first free slot in the stack */ global_State *l_G; CallInfo *ci; /* call info for current function */ - StkId stack_last; /* end of stack (last element + 1) */ - StkId stack; /* stack base */ + StkIdRel stack_last; /* end of stack (last element + 1) */ + StkIdRel stack; /* stack base */ UpVal *openupval; /* list of open upvalues in this stack */ - StkId tbclist; /* list of to-be-closed variables */ + StkIdRel tbclist; /* list of to-be-closed variables */ GCObject *gclist; struct lua_State *twups; /* list of threads with open upvalues */ struct lua_longjmp *errorJmp; /* current error recover point */ diff --git a/src/lua/lstrlib.c b/src/lua/lstrlib.c index 0b4fdbb7b..03167161d 100644 --- a/src/lua/lstrlib.c +++ b/src/lua/lstrlib.c @@ -570,7 +570,7 @@ static const char *match_capture (MatchState *ms, const char *s, int l) { static const char *match (MatchState *ms, const char *s, const char *p) { if (l_unlikely(ms->matchdepth-- == 0)) luaL_error(ms->L, "pattern too complex"); - init: /* using goto's to optimize tail recursion */ + init: /* using goto to optimize tail recursion */ if (p != ms->p_end) { /* end of pattern? */ switch (*p) { case '(': { /* start capture */ diff --git a/src/lua/ltable.c b/src/lua/ltable.c index 1b1cd2415..3c690c5f1 100644 --- a/src/lua/ltable.c +++ b/src/lua/ltable.c @@ -107,7 +107,7 @@ static const TValue absentkey = {ABSTKEYCONSTANT}; */ static Node *hashint (const Table *t, lua_Integer i) { lua_Unsigned ui = l_castS2U(i); - if (ui <= (unsigned int)INT_MAX) + if (ui <= cast_uint(INT_MAX)) return hashmod(t, cast_int(ui)); else return hashmod(t, ui); @@ -257,9 +257,11 @@ LUAI_FUNC unsigned int luaH_realasize (const Table *t) { size |= (size >> 2); size |= (size >> 4); size |= (size >> 8); +#if (UINT_MAX >> 14) > 3 /* unsigned int has more than 16 bits */ size |= (size >> 16); #if (UINT_MAX >> 30) > 3 size |= (size >> 32); /* unsigned int has more than 32 bits */ +#endif #endif size++; lua_assert(ispow2(size) && size/2 < t->alimit && t->alimit < size); @@ -488,7 +490,7 @@ static void setnodevector (lua_State *L, Table *t, unsigned int size) { luaG_runerror(L, "table overflow"); size = twoto(lsize); t->node = luaM_newvector(L, size, Node); - for (i = 0; i < (int)size; i++) { + for (i = 0; i < cast_int(size); i++) { Node *n = gnode(t, i); gnext(n) = 0; setnilkey(n); @@ -975,6 +977,4 @@ Node *luaH_mainposition (const Table *t, const TValue *key) { return mainpositionTV(t, key); } -int luaH_isdummy (const Table *t) { return isdummy(t); } - #endif diff --git a/src/lua/ltable.h b/src/lua/ltable.h index 7bbbcb213..75dd9e26e 100644 --- a/src/lua/ltable.h +++ b/src/lua/ltable.h @@ -59,7 +59,6 @@ LUAI_FUNC unsigned int luaH_realasize (const Table *t); #if defined(LUA_DEBUG) LUAI_FUNC Node *luaH_mainposition (const Table *t, const TValue *key); -LUAI_FUNC int luaH_isdummy (const Table *t); #endif diff --git a/src/lua/ltablib.c b/src/lua/ltablib.c index 868d78fd8..e6bc4d04a 100644 --- a/src/lua/ltablib.c +++ b/src/lua/ltablib.c @@ -93,7 +93,7 @@ static int tremove (lua_State *L) { lua_Integer pos = luaL_optinteger(L, 2, size); if (pos != size) /* validate 'pos' if given */ /* check whether 'pos' is in [1, size + 1] */ - luaL_argcheck(L, (lua_Unsigned)pos - 1u <= (lua_Unsigned)size, 1, + luaL_argcheck(L, (lua_Unsigned)pos - 1u <= (lua_Unsigned)size, 2, "position out of bounds"); lua_geti(L, 1, pos); /* result = t[pos] */ for ( ; pos < size; pos++) { diff --git a/src/lua/ltm.c b/src/lua/ltm.c index b657b783a..07a060811 100644 --- a/src/lua/ltm.c +++ b/src/lua/ltm.c @@ -102,12 +102,12 @@ const char *luaT_objtypename (lua_State *L, const TValue *o) { void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1, const TValue *p2, const TValue *p3) { - StkId func = L->top; + StkId func = L->top.p; setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */ setobj2s(L, func + 1, p1); /* 1st argument */ setobj2s(L, func + 2, p2); /* 2nd argument */ setobj2s(L, func + 3, p3); /* 3rd argument */ - L->top = func + 4; + L->top.p = func + 4; /* metamethod may yield only when called from Lua code */ if (isLuacode(L->ci)) luaD_call(L, func, 0); @@ -119,18 +119,18 @@ void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1, void luaT_callTMres (lua_State *L, const TValue *f, const TValue *p1, const TValue *p2, StkId res) { ptrdiff_t result = savestack(L, res); - StkId func = L->top; + StkId func = L->top.p; setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */ setobj2s(L, func + 1, p1); /* 1st argument */ setobj2s(L, func + 2, p2); /* 2nd argument */ - L->top += 3; + L->top.p += 3; /* metamethod may yield only when called from Lua code */ if (isLuacode(L->ci)) luaD_call(L, func, 1); else luaD_callnoyield(L, func, 1); res = restorestack(L, result); - setobjs2s(L, res, --L->top); /* move result to its place */ + setobjs2s(L, res, --L->top.p); /* move result to its place */ } @@ -165,7 +165,7 @@ void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2, void luaT_tryconcatTM (lua_State *L) { - StkId top = L->top; + StkId top = L->top.p; if (l_unlikely(!callbinTM(L, s2v(top - 2), s2v(top - 1), top - 2, TM_CONCAT))) luaG_concaterror(L, s2v(top - 2), s2v(top - 1)); @@ -200,15 +200,15 @@ void luaT_trybiniTM (lua_State *L, const TValue *p1, lua_Integer i2, */ int luaT_callorderTM (lua_State *L, const TValue *p1, const TValue *p2, TMS event) { - if (callbinTM(L, p1, p2, L->top, event)) /* try original event */ - return !l_isfalse(s2v(L->top)); + if (callbinTM(L, p1, p2, L->top.p, event)) /* try original event */ + return !l_isfalse(s2v(L->top.p)); #if defined(LUA_COMPAT_LT_LE) else if (event == TM_LE) { /* try '!(p2 < p1)' for '(p1 <= p2)' */ L->ci->callstatus |= CIST_LEQ; /* mark it is doing 'lt' for 'le' */ - if (callbinTM(L, p2, p1, L->top, TM_LT)) { + if (callbinTM(L, p2, p1, L->top.p, TM_LT)) { L->ci->callstatus ^= CIST_LEQ; /* clear mark */ - return l_isfalse(s2v(L->top)); + return l_isfalse(s2v(L->top.p)); } /* else error will remove this 'ci'; no need to clear mark */ } @@ -238,20 +238,20 @@ int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2, void luaT_adjustvarargs (lua_State *L, int nfixparams, CallInfo *ci, const Proto *p) { int i; - int actual = cast_int(L->top - ci->func) - 1; /* number of arguments */ + int actual = cast_int(L->top.p - ci->func.p) - 1; /* number of arguments */ int nextra = actual - nfixparams; /* number of extra arguments */ ci->u.l.nextraargs = nextra; luaD_checkstack(L, p->maxstacksize + 1); /* copy function to the top of the stack */ - setobjs2s(L, L->top++, ci->func); + setobjs2s(L, L->top.p++, ci->func.p); /* move fixed parameters to the top of the stack */ for (i = 1; i <= nfixparams; i++) { - setobjs2s(L, L->top++, ci->func + i); - setnilvalue(s2v(ci->func + i)); /* erase original parameter (for GC) */ + setobjs2s(L, L->top.p++, ci->func.p + i); + setnilvalue(s2v(ci->func.p + i)); /* erase original parameter (for GC) */ } - ci->func += actual + 1; - ci->top += actual + 1; - lua_assert(L->top <= ci->top && ci->top <= L->stack_last); + ci->func.p += actual + 1; + ci->top.p += actual + 1; + lua_assert(L->top.p <= ci->top.p && ci->top.p <= L->stack_last.p); } @@ -261,10 +261,10 @@ void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted) { if (wanted < 0) { wanted = nextra; /* get all extra arguments available */ checkstackGCp(L, nextra, where); /* ensure stack space */ - L->top = where + nextra; /* next instruction will need top */ + L->top.p = where + nextra; /* next instruction will need top */ } for (i = 0; i < wanted && i < nextra; i++) - setobjs2s(L, where + i, ci->func - nextra + i); + setobjs2s(L, where + i, ci->func.p - nextra + i); for (; i < wanted; i++) /* complete required results with nil */ setnilvalue(s2v(where + i)); } diff --git a/src/lua/ltm.h b/src/lua/ltm.h index 73b833c60..c309e2ae1 100644 --- a/src/lua/ltm.h +++ b/src/lua/ltm.h @@ -9,6 +9,7 @@ #include "lobject.h" +#include "lstate.h" /* @@ -95,8 +96,8 @@ LUAI_FUNC int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2, int inv, int isfloat, TMS event); LUAI_FUNC void luaT_adjustvarargs (lua_State *L, int nfixparams, - struct CallInfo *ci, const Proto *p); -LUAI_FUNC void luaT_getvarargs (lua_State *L, struct CallInfo *ci, + CallInfo *ci, const Proto *p); +LUAI_FUNC void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted); diff --git a/src/lua/lua.c b/src/lua/lua.c index 03815a054..0ff884545 100644 --- a/src/lua/lua.c +++ b/src/lua/lua.c @@ -20,10 +20,6 @@ #include "lauxlib.h" #include "lualib.h" -/** Pi-hole modification **/ -#include "ftl_lua.h" -/**************************/ - #if !defined(LUA_PROGNAME) #define LUA_PROGNAME "lua" @@ -181,10 +177,11 @@ static void print_version (void) { ** to the script (everything after 'script') go to positive indices; ** other arguments (before the script name) go to negative indices. ** If there is no script name, assume interpreter's name as base. +** (If there is no interpreter's name either, 'script' is -1, so +** table sizes are zero.) */ static void createargtable (lua_State *L, char **argv, int argc, int script) { int i, narg; - if (script == argc) script = 0; /* no script name? */ narg = argc - (script + 1); /* number of positive indices */ lua_createtable(L, narg, script + 1); for (i = 0; i < argc; i++) { @@ -214,9 +211,7 @@ static int dostring (lua_State *L, const char *s, const char *name) { /* ** Receives 'globname[=modname]' and runs 'globname = require(modname)'. */ -/************** Pi-hole modification ***************/ -int dolibrary (lua_State *L, char *globname) { -/***************************************************/ +static int dolibrary (lua_State *L, char *globname) { int status; char *modname = strchr(globname, '='); if (modname == NULL) /* no explicit name? */ @@ -274,14 +269,23 @@ static int handle_script (lua_State *L, char **argv) { /* ** Traverses all arguments from 'argv', returning a mask with those -** needed before running any Lua code (or an error code if it finds -** any invalid argument). 'first' returns the first not-handled argument -** (either the script name or a bad argument in case of error). +** needed before running any Lua code or an error code if it finds any +** invalid argument. In case of error, 'first' is the index of the bad +** argument. Otherwise, 'first' is -1 if there is no program name, +** 0 if there is no script name, or the index of the script name. */ static int collectargs (char **argv, int *first) { int args = 0; int i; - for (i = 1; argv[i] != NULL; i++) { + if (argv[0] != NULL) { /* is there a program name? */ + if (argv[0][0]) /* not empty? */ + progname = argv[0]; /* save it */ + } + else { /* no program name */ + *first = -1; + return 0; + } + for (i = 1; argv[i] != NULL; i++) { /* handle arguments */ *first = i; if (argv[i][0] != '-') /* not an option? */ return args; /* stop handling options */ @@ -322,7 +326,7 @@ static int collectargs (char **argv, int *first) { return has_error; } } - *first = i; /* no script name */ + *first = 0; /* no script name */ return args; } @@ -615,8 +619,8 @@ static int pmain (lua_State *L) { char **argv = (char **)lua_touserdata(L, 2); int script; int args = collectargs(argv, &script); + int optlim = (script > 0) ? script : argc; /* first argv not an option */ luaL_checkversion(L); /* check that interpreter has correct version */ - if (argv[0] && argv[0][0]) progname = argv[0]; if (args == has_error) { /* bad arg? */ print_usage(argv[script]); /* 'script' has index of bad arg. */ return 0; @@ -629,25 +633,21 @@ static int pmain (lua_State *L) { } luaL_openlibs(L); /* open standard libraries */ createargtable(L, argv, argc, script); /* create table 'arg' */ - lua_gc(L, LUA_GCGEN, 0, 0); /* GC in generational mode */ + lua_gc(L, LUA_GCRESTART); /* start GC... */ + lua_gc(L, LUA_GCGEN, 0, 0); /* ...in generational mode */ if (!(args & has_E)) { /* no option '-E'? */ if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */ return 0; /* error running LUA_INIT */ } - - /************** Pi-hole modification ***************/ - // Load and enable libraries bundled with Pi-hole - ftl_lua_init(L); - /***************************************************/ - - if (!runargs(L, argv, script)) /* execute arguments -e and -l */ + if (!runargs(L, argv, optlim)) /* execute arguments -e and -l */ return 0; /* something failed */ - if (script < argc && /* execute main script (if there is one) */ - handle_script(L, argv + script) != LUA_OK) - return 0; + if (script > 0) { /* execute main script (if there is one) */ + if (handle_script(L, argv + script) != LUA_OK) + return 0; /* interrupt in case of error */ + } if (args & has_i) /* -i option? */ doREPL(L); /* do read-eval-print loop */ - else if (script == argc && !(args & (has_e | has_v))) { /* no arguments? */ + else if (script < 1 && !(args & (has_e | has_v))) { /* no active option? */ if (lua_stdin_is_tty()) { /* running in interactive mode? */ print_version(); doREPL(L); /* do read-eval-print loop */ @@ -659,15 +659,14 @@ static int pmain (lua_State *L) { } -/******* Pi-hole modification ********/ -int lua_main (int argc, char **argv) { -/*************************************/ +int main (int argc, char **argv) { int status, result; lua_State *L = luaL_newstate(); /* create state */ if (L == NULL) { l_message(argv[0], "cannot create state: not enough memory"); return EXIT_FAILURE; } + lua_gc(L, LUA_GCSTOP); /* stop GC while building state */ lua_pushcfunction(L, &pmain); /* to call 'pmain' in protected mode */ lua_pushinteger(L, argc); /* 1st argument */ lua_pushlightuserdata(L, argv); /* 2nd argument */ diff --git a/src/lua/lua.h b/src/lua/lua.h index e6618392c..01927c6d9 100644 --- a/src/lua/lua.h +++ b/src/lua/lua.h @@ -18,14 +18,14 @@ #define LUA_VERSION_MAJOR "5" #define LUA_VERSION_MINOR "4" -#define LUA_VERSION_RELEASE "4" +#define LUA_VERSION_RELEASE "5" #define LUA_VERSION_NUM 504 -#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 4) +#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 5) #define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR #define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE -#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2022 Lua.org, PUC-Rio" +#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2023 Lua.org, PUC-Rio" #define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes" @@ -131,6 +131,16 @@ typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); typedef void (*lua_WarnFunction) (void *ud, const char *msg, int tocont); +/* +** Type used by the debug API to collect debug information +*/ +typedef struct lua_Debug lua_Debug; + + +/* +** Functions to be called by the debugger in specific events +*/ +typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar); /* @@ -153,7 +163,7 @@ extern const char lua_ident[]; 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 int (lua_resetthread) (lua_State *L); +LUA_API int (lua_resetthread) (lua_State *L, lua_State *from); LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf); @@ -442,12 +452,6 @@ LUA_API void (lua_closeslot) (lua_State *L, int idx); #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 debugger 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); @@ -492,7 +496,7 @@ struct lua_Debug { /****************************************************************************** -* Copyright (C) 1994-2022 Lua.org, PUC-Rio. +* Copyright (C) 1994-2023 Lua.org, PUC-Rio. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the diff --git a/src/lua/luac.c b/src/lua/luac.c index 6b556b267..5f4a141bd 100644 --- a/src/lua/luac.c +++ b/src/lua/luac.c @@ -121,7 +121,7 @@ static int doargs(int argc, char* argv[]) return i; } -#define FUNCTION "(function()end)();" +#define FUNCTION "(function()end)();\n" static const char* reader(lua_State* L, void* ud, size_t* size) { @@ -138,7 +138,7 @@ static const char* reader(lua_State* L, void* ud, size_t* size) } } -#define toproto(L,i) getproto(s2v(L->top+(i))) +#define toproto(L,i) getproto(s2v(L->top.p+(i))) static const Proto* combine(lua_State* L, int n) { @@ -155,8 +155,6 @@ static const Proto* combine(lua_State* L, int n) f->p[i]=toproto(L,i-n-1); if (f->p[i]->sizeupvalues>0) f->p[i]->upvalues[0].instack=0; } - luaM_freearray(L,f->lineinfo,f->sizelineinfo); - f->sizelineinfo=0; return f; } } @@ -195,9 +193,7 @@ static int pmain(lua_State* L) return 0; } -/******* Pi-hole modification ********/ -int luac_main(int argc, char* argv[]) -/*************************************/ +int main(int argc, char* argv[]) { lua_State* L; int i=doargs(argc,argv); diff --git a/src/lua/luaconf.h b/src/lua/luaconf.h index d42d14b7d..137103ede 100644 --- a/src/lua/luaconf.h +++ b/src/lua/luaconf.h @@ -70,6 +70,12 @@ #endif +#if defined(LUA_USE_IOS) +#define LUA_USE_POSIX +#define LUA_USE_DLOPEN +#endif + + /* @@ LUAI_IS32INT is true iff 'int' has (at least) 32 bits. */ @@ -728,7 +734,7 @@ ** CHANGE it if you need a different limit. This limit is arbitrary; ** its only purpose is to stop Lua from consuming unlimited stack ** space (and to reserve some numbers for pseudo-indices). -** (It must fit into max(size_t)/32.) +** (It must fit into max(size_t)/32 and max(int)/2.) */ #if LUAI_IS32INT #define LUAI_MAXSTACK 1000000 @@ -747,14 +753,15 @@ /* @@ LUA_IDSIZE gives the maximum size for the description of the source -@@ of a function in debug information. +** of a function in debug information. ** CHANGE it if you want a different size. */ #define LUA_IDSIZE 60 /* -@@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system. +@@ LUAL_BUFFERSIZE is the initial buffer size used by the lauxlib +** buffer system. */ #define LUAL_BUFFERSIZE ((int)(16 * sizeof(void*) * sizeof(lua_Number))) diff --git a/src/lua/lualib.h b/src/lua/lualib.h index a7cab4eb0..262552907 100644 --- a/src/lua/lualib.h +++ b/src/lua/lualib.h @@ -44,10 +44,6 @@ LUAMOD_API int (luaopen_debug) (lua_State *L); #define LUA_LOADLIBNAME "package" LUAMOD_API int (luaopen_package) (lua_State *L); -/************ Pi-hole modification *************/ -#define LUA_PIHOLELIBNAME "pihole" -LUAMOD_API int (luaopen_pihole) (lua_State *L); -/***********************************************/ /* open all previous libraries */ LUALIB_API void (luaL_openlibs) (lua_State *L); diff --git a/src/lua/lundump.c b/src/lua/lundump.c index 5aa55c445..02aed64fb 100644 --- a/src/lua/lundump.c +++ b/src/lua/lundump.c @@ -120,10 +120,10 @@ static TString *loadStringN (LoadState *S, Proto *p) { } else { /* long string */ ts = luaS_createlngstrobj(L, size); /* create string */ - setsvalue2s(L, L->top, ts); /* anchor it ('loadVector' can GC) */ + setsvalue2s(L, L->top.p, ts); /* anchor it ('loadVector' can GC) */ luaD_inctop(L); loadVector(S, getstr(ts), size); /* load directly in final place */ - L->top--; /* pop string */ + L->top.p--; /* pop string */ } luaC_objbarrier(L, p, ts); return ts; @@ -248,6 +248,8 @@ static void loadDebug (LoadState *S, Proto *f) { f->locvars[i].endpc = loadInt(S); } n = loadInt(S); + if (n != 0) /* does it have debug information? */ + n = f->sizeupvalues; /* must be this many */ for (i = 0; i < n; i++) f->upvalues[i].name = loadStringN(S, f); } @@ -321,7 +323,7 @@ LClosure *luaU_undump(lua_State *L, ZIO *Z, const char *name) { S.Z = Z; checkHeader(&S); cl = luaF_newLclosure(L, loadByte(&S)); - setclLvalue2s(L, L->top, cl); + setclLvalue2s(L, L->top.p, cl); luaD_inctop(L); cl->p = luaF_newproto(L); luaC_objbarrier(L, cl, cl->p); diff --git a/src/lua/lutf8lib.c b/src/lua/lutf8lib.c index e7bf098f6..3a5b9bc38 100644 --- a/src/lua/lutf8lib.c +++ b/src/lua/lutf8lib.c @@ -25,6 +25,9 @@ #define MAXUTF 0x7FFFFFFFu + +#define MSGInvalid "invalid UTF-8 code" + /* ** Integer type for decoded UTF-8 values; MAXUTF needs 31 bits. */ @@ -35,7 +38,8 @@ typedef unsigned long utfint; #endif -#define iscont(p) ((*(p) & 0xC0) == 0x80) +#define iscont(c) (((c) & 0xC0) == 0x80) +#define iscontp(p) iscont(*(p)) /* from strlib */ @@ -65,7 +69,7 @@ static const char *utf8_decode (const char *s, utfint *val, int strict) { int count = 0; /* to count number of continuation bytes */ for (; c & 0x40; c <<= 1) { /* while it needs continuation bytes... */ unsigned int cc = (unsigned char)s[++count]; /* read next byte */ - if ((cc & 0xC0) != 0x80) /* not a continuation byte? */ + if (!iscont(cc)) /* not a continuation byte? */ return NULL; /* invalid byte sequence */ res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */ } @@ -140,7 +144,7 @@ static int codepoint (lua_State *L) { utfint code; s = utf8_decode(s, &code, !lax); if (s == NULL) - return luaL_error(L, "invalid UTF-8 code"); + return luaL_error(L, MSGInvalid); lua_pushinteger(L, code); n++; } @@ -190,16 +194,16 @@ static int byteoffset (lua_State *L) { "position out of bounds"); if (n == 0) { /* find beginning of current byte sequence */ - while (posi > 0 && iscont(s + posi)) posi--; + while (posi > 0 && iscontp(s + posi)) posi--; } else { - if (iscont(s + posi)) + if (iscontp(s + posi)) return luaL_error(L, "initial position is a continuation byte"); if (n < 0) { while (n < 0 && posi > 0) { /* move back */ do { /* find beginning of previous character */ posi--; - } while (posi > 0 && iscont(s + posi)); + } while (posi > 0 && iscontp(s + posi)); n++; } } @@ -208,7 +212,7 @@ static int byteoffset (lua_State *L) { while (n > 0 && posi < (lua_Integer)len) { do { /* find beginning of next character */ posi++; - } while (iscont(s + posi)); /* (cannot pass final '\0') */ + } while (iscontp(s + posi)); /* (cannot pass final '\0') */ n--; } } @@ -226,15 +230,15 @@ static int iter_aux (lua_State *L, int strict) { const char *s = luaL_checklstring(L, 1, &len); lua_Unsigned n = (lua_Unsigned)lua_tointeger(L, 2); if (n < len) { - while (iscont(s + n)) n++; /* skip continuation bytes */ + while (iscontp(s + n)) n++; /* go to next character */ } if (n >= len) /* (also handles original 'n' being negative) */ return 0; /* no more codepoints */ else { utfint code; const char *next = utf8_decode(s + n, &code, strict); - if (next == NULL) - return luaL_error(L, "invalid UTF-8 code"); + if (next == NULL || iscontp(next)) + return luaL_error(L, MSGInvalid); lua_pushinteger(L, n + 1); lua_pushinteger(L, code); return 2; @@ -253,7 +257,8 @@ static int iter_auxlax (lua_State *L) { static int iter_codes (lua_State *L) { int lax = lua_toboolean(L, 2); - luaL_checkstring(L, 1); + const char *s = luaL_checkstring(L, 1); + luaL_argcheck(L, !iscontp(s), 1, MSGInvalid); lua_pushcfunction(L, lax ? iter_auxlax : iter_auxstrict); lua_pushvalue(L, 1); lua_pushinteger(L, 0); diff --git a/src/lua/lvm.c b/src/lua/lvm.c index 2ec344003..8493a770c 100644 --- a/src/lua/lvm.c +++ b/src/lua/lvm.c @@ -608,8 +608,8 @@ int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) { if (tm == NULL) /* no TM? */ return 0; /* objects are different */ else { - luaT_callTMres(L, tm, t1, t2, L->top); /* call TM */ - return !l_isfalse(s2v(L->top)); + luaT_callTMres(L, tm, t1, t2, L->top.p); /* call TM */ + return !l_isfalse(s2v(L->top.p)); } } @@ -633,17 +633,17 @@ static void copy2buff (StkId top, int n, char *buff) { /* ** Main operation for concatenation: concat 'total' values in the stack, -** from 'L->top - total' up to 'L->top - 1'. +** from 'L->top.p - total' up to 'L->top.p - 1'. */ void luaV_concat (lua_State *L, int total) { if (total == 1) return; /* "all" values already concatenated */ do { - StkId top = L->top; + StkId top = L->top.p; int n = 2; /* number of elements handled in this pass (at least 2) */ if (!(ttisstring(s2v(top - 2)) || cvt2str(s2v(top - 2))) || !tostring(L, s2v(top - 1))) - luaT_tryconcatTM(L); + luaT_tryconcatTM(L); /* may invalidate 'top' */ else if (isemptystr(s2v(top - 1))) /* second operand is empty? */ cast_void(tostring(L, s2v(top - 2))); /* result is first operand */ else if (isemptystr(s2v(top - 2))) { /* first operand is empty string? */ @@ -656,8 +656,10 @@ void luaV_concat (lua_State *L, int total) { /* collect total length and number of strings */ for (n = 1; n < total && tostring(L, s2v(top - n - 1)); n++) { size_t l = vslen(s2v(top - n - 1)); - if (l_unlikely(l >= (MAX_SIZE/sizeof(char)) - tl)) + if (l_unlikely(l >= (MAX_SIZE/sizeof(char)) - tl)) { + L->top.p = top - total; /* pop strings to avoid wasting stack */ luaG_runerror(L, "string length overflow"); + } tl += l; } if (tl <= LUAI_MAXSHORTLEN) { /* is result a short string? */ @@ -671,8 +673,8 @@ void luaV_concat (lua_State *L, int total) { } setsvalue2s(L, top - n, ts); /* create result */ } - total -= n-1; /* got 'n' strings to create 1 new */ - L->top -= n-1; /* popped 'n' strings and pushed one */ + total -= n - 1; /* got 'n' strings to create one new */ + L->top.p -= n - 1; /* popped 'n' strings and pushed one */ } while (total > 1); /* repeat until only 1 result left */ } @@ -763,12 +765,10 @@ lua_Number luaV_modf (lua_State *L, lua_Number m, lua_Number n) { /* number of bits in an integer */ #define NBITS cast_int(sizeof(lua_Integer) * CHAR_BIT) + /* ** Shift left operation. (Shift right just negates 'y'.) */ -#define luaV_shiftr(x,y) luaV_shiftl(x,intop(-, 0, y)) - - lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y) { if (y < 0) { /* shift right? */ if (y <= -NBITS) return 0; @@ -808,26 +808,26 @@ static void pushclosure (lua_State *L, Proto *p, UpVal **encup, StkId base, */ void luaV_finishOp (lua_State *L) { CallInfo *ci = L->ci; - StkId base = ci->func + 1; + StkId base = ci->func.p + 1; Instruction inst = *(ci->u.l.savedpc - 1); /* interrupted instruction */ OpCode op = GET_OPCODE(inst); switch (op) { /* finish its execution */ case OP_MMBIN: case OP_MMBINI: case OP_MMBINK: { - setobjs2s(L, base + GETARG_A(*(ci->u.l.savedpc - 2)), --L->top); + setobjs2s(L, base + GETARG_A(*(ci->u.l.savedpc - 2)), --L->top.p); break; } case OP_UNM: case OP_BNOT: case OP_LEN: case OP_GETTABUP: case OP_GETTABLE: case OP_GETI: case OP_GETFIELD: case OP_SELF: { - setobjs2s(L, base + GETARG_A(inst), --L->top); + setobjs2s(L, base + GETARG_A(inst), --L->top.p); break; } case OP_LT: case OP_LE: case OP_LTI: case OP_LEI: case OP_GTI: case OP_GEI: case OP_EQ: { /* note that 'OP_EQI'/'OP_EQK' cannot yield */ - int res = !l_isfalse(s2v(L->top - 1)); - L->top--; + int res = !l_isfalse(s2v(L->top.p - 1)); + L->top.p--; #if defined(LUA_COMPAT_LT_LE) if (ci->callstatus & CIST_LEQ) { /* "<=" using "<" instead? */ ci->callstatus ^= CIST_LEQ; /* clear mark */ @@ -840,11 +840,11 @@ void luaV_finishOp (lua_State *L) { break; } case OP_CONCAT: { - StkId top = L->top - 1; /* top when 'luaT_tryconcatTM' was called */ + StkId top = L->top.p - 1; /* top when 'luaT_tryconcatTM' was called */ int a = GETARG_A(inst); /* first element to concatenate */ int total = cast_int(top - 1 - (base + a)); /* yet to concatenate */ setobjs2s(L, top - 2, top); /* put TM result in proper position */ - L->top = top - 1; /* top is one after last element (at top-2) */ + L->top.p = top - 1; /* top is one after last element (at top-2) */ luaV_concat(L, total); /* concat them (may yield again) */ break; } @@ -856,7 +856,7 @@ void luaV_finishOp (lua_State *L) { StkId ra = base + GETARG_A(inst); /* adjust top to signal correct number of returns, in case the return is "up to top" ('isIT') */ - L->top = ra + ci->u2.nres; + L->top.p = ra + ci->u2.nres; /* repeat instruction to close other vars. and complete the return */ ci->u.l.savedpc--; break; @@ -898,6 +898,7 @@ void luaV_finishOp (lua_State *L) { ** operation, 'fop' is the float operation. */ #define op_arithI(L,iop,fop) { \ + StkId ra = RA(i); \ TValue *v1 = vRB(i); \ int imm = GETARG_sC(i); \ if (ttisinteger(v1)) { \ @@ -926,6 +927,7 @@ void luaV_finishOp (lua_State *L) { ** Arithmetic operations over floats and others with register operands. */ #define op_arithf(L,fop) { \ + StkId ra = RA(i); \ TValue *v1 = vRB(i); \ TValue *v2 = vRC(i); \ op_arithf_aux(L, v1, v2, fop); } @@ -935,6 +937,7 @@ void luaV_finishOp (lua_State *L) { ** Arithmetic operations with K operands for floats. */ #define op_arithfK(L,fop) { \ + StkId ra = RA(i); \ TValue *v1 = vRB(i); \ TValue *v2 = KC(i); lua_assert(ttisnumber(v2)); \ op_arithf_aux(L, v1, v2, fop); } @@ -944,6 +947,7 @@ void luaV_finishOp (lua_State *L) { ** Arithmetic operations over integers and floats. */ #define op_arith_aux(L,v1,v2,iop,fop) { \ + StkId ra = RA(i); \ if (ttisinteger(v1) && ttisinteger(v2)) { \ lua_Integer i1 = ivalue(v1); lua_Integer i2 = ivalue(v2); \ pc++; setivalue(s2v(ra), iop(L, i1, i2)); \ @@ -973,6 +977,7 @@ void luaV_finishOp (lua_State *L) { ** Bitwise operations with constant operand. */ #define op_bitwiseK(L,op) { \ + StkId ra = RA(i); \ TValue *v1 = vRB(i); \ TValue *v2 = KC(i); \ lua_Integer i1; \ @@ -986,6 +991,7 @@ void luaV_finishOp (lua_State *L) { ** Bitwise operations with register operands. */ #define op_bitwise(L,op) { \ + StkId ra = RA(i); \ TValue *v1 = vRB(i); \ TValue *v2 = vRC(i); \ lua_Integer i1; lua_Integer i2; \ @@ -1000,18 +1006,19 @@ void luaV_finishOp (lua_State *L) { ** integers. */ #define op_order(L,opi,opn,other) { \ - int cond; \ - TValue *rb = vRB(i); \ - if (ttisinteger(s2v(ra)) && ttisinteger(rb)) { \ - lua_Integer ia = ivalue(s2v(ra)); \ - lua_Integer ib = ivalue(rb); \ - cond = opi(ia, ib); \ - } \ - else if (ttisnumber(s2v(ra)) && ttisnumber(rb)) \ - cond = opn(s2v(ra), rb); \ - else \ - Protect(cond = other(L, s2v(ra), rb)); \ - docondjump(); } + StkId ra = RA(i); \ + int cond; \ + TValue *rb = vRB(i); \ + if (ttisinteger(s2v(ra)) && ttisinteger(rb)) { \ + lua_Integer ia = ivalue(s2v(ra)); \ + lua_Integer ib = ivalue(rb); \ + cond = opi(ia, ib); \ + } \ + else if (ttisnumber(s2v(ra)) && ttisnumber(rb)) \ + cond = opn(s2v(ra), rb); \ + else \ + Protect(cond = other(L, s2v(ra), rb)); \ + docondjump(); } /* @@ -1019,20 +1026,21 @@ void luaV_finishOp (lua_State *L) { ** always small enough to have an exact representation as a float.) */ #define op_orderI(L,opi,opf,inv,tm) { \ - int cond; \ - int im = GETARG_sB(i); \ - if (ttisinteger(s2v(ra))) \ - cond = opi(ivalue(s2v(ra)), im); \ - else if (ttisfloat(s2v(ra))) { \ - lua_Number fa = fltvalue(s2v(ra)); \ - lua_Number fim = cast_num(im); \ - cond = opf(fa, fim); \ - } \ - else { \ - int isf = GETARG_C(i); \ - Protect(cond = luaT_callorderiTM(L, s2v(ra), im, inv, isf, tm)); \ - } \ - docondjump(); } + StkId ra = RA(i); \ + int cond; \ + int im = GETARG_sB(i); \ + if (ttisinteger(s2v(ra))) \ + cond = opi(ivalue(s2v(ra)), im); \ + else if (ttisfloat(s2v(ra))) { \ + lua_Number fa = fltvalue(s2v(ra)); \ + lua_Number fim = cast_num(im); \ + cond = opf(fa, fim); \ + } \ + else { \ + int isf = GETARG_C(i); \ + Protect(cond = luaT_callorderiTM(L, s2v(ra), im, inv, isf, tm)); \ + } \ + docondjump(); } /* }================================================================== */ @@ -1061,7 +1069,7 @@ void luaV_finishOp (lua_State *L) { #define updatetrap(ci) (trap = ci->u.l.trap) -#define updatebase(ci) (base = ci->func + 1) +#define updatebase(ci) (base = ci->func.p + 1) #define updatestack(ci) \ @@ -1096,7 +1104,7 @@ void luaV_finishOp (lua_State *L) { ** Whenever code can raise errors, the global 'pc' and the global ** 'top' must be correct to report occasional errors. */ -#define savestate(L,ci) (savepc(L), L->top = ci->top) +#define savestate(L,ci) (savepc(L), L->top.p = ci->top.p) /* @@ -1116,7 +1124,7 @@ void luaV_finishOp (lua_State *L) { /* 'c' is the limit of live values in the stack */ #define checkGC(L,c) \ - { luaC_condGC(L, (savepc(L), L->top = (c)), \ + { luaC_condGC(L, (savepc(L), L->top.p = (c)), \ updatetrap(ci)); \ luai_threadyield(L); } @@ -1128,7 +1136,6 @@ void luaV_finishOp (lua_State *L) { updatebase(ci); /* correct stack */ \ } \ i = *(pc++); \ - ra = RA(i); /* WARNING: any stack reallocation invalidates 'ra' */ \ } #define vmdispatch(o) switch(o) @@ -1148,7 +1155,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { startfunc: trap = L->hookmask; returning: /* trap already set */ - cl = clLvalue(s2v(ci->func)); + cl = clLvalue(s2v(ci->func.p)); k = cl->p->k; pc = ci->u.l.savedpc; if (l_unlikely(trap)) { @@ -1160,60 +1167,68 @@ void luaV_execute (lua_State *L, CallInfo *ci) { } ci->u.l.trap = 1; /* assume trap is on, for now */ } - base = ci->func + 1; + base = ci->func.p + 1; /* main loop of interpreter */ for (;;) { Instruction i; /* instruction being executed */ - StkId ra; /* instruction's A register */ vmfetch(); #if 0 /* low-level line tracing for debugging Lua */ printf("line: %d\n", luaG_getfuncline(cl->p, pcRel(pc, cl->p))); #endif - lua_assert(base == ci->func + 1); - lua_assert(base <= L->top && L->top < L->stack_last); + lua_assert(base == ci->func.p + 1); + lua_assert(base <= L->top.p && L->top.p <= L->stack_last.p); /* invalidate top for instructions not expecting it */ - lua_assert(isIT(i) || (cast_void(L->top = base), 1)); + lua_assert(isIT(i) || (cast_void(L->top.p = base), 1)); vmdispatch (GET_OPCODE(i)) { vmcase(OP_MOVE) { + StkId ra = RA(i); setobjs2s(L, ra, RB(i)); vmbreak; } vmcase(OP_LOADI) { + StkId ra = RA(i); lua_Integer b = GETARG_sBx(i); setivalue(s2v(ra), b); vmbreak; } vmcase(OP_LOADF) { + StkId ra = RA(i); int b = GETARG_sBx(i); setfltvalue(s2v(ra), cast_num(b)); vmbreak; } vmcase(OP_LOADK) { + StkId ra = RA(i); TValue *rb = k + GETARG_Bx(i); setobj2s(L, ra, rb); vmbreak; } vmcase(OP_LOADKX) { + StkId ra = RA(i); TValue *rb; rb = k + GETARG_Ax(*pc); pc++; setobj2s(L, ra, rb); vmbreak; } vmcase(OP_LOADFALSE) { + StkId ra = RA(i); setbfvalue(s2v(ra)); vmbreak; } vmcase(OP_LFALSESKIP) { + StkId ra = RA(i); setbfvalue(s2v(ra)); pc++; /* skip next instruction */ vmbreak; } vmcase(OP_LOADTRUE) { + StkId ra = RA(i); setbtvalue(s2v(ra)); vmbreak; } vmcase(OP_LOADNIL) { + StkId ra = RA(i); int b = GETARG_B(i); do { setnilvalue(s2v(ra++)); @@ -1221,19 +1236,22 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_GETUPVAL) { + StkId ra = RA(i); int b = GETARG_B(i); - setobj2s(L, ra, cl->upvals[b]->v); + setobj2s(L, ra, cl->upvals[b]->v.p); vmbreak; } vmcase(OP_SETUPVAL) { + StkId ra = RA(i); UpVal *uv = cl->upvals[GETARG_B(i)]; - setobj(L, uv->v, s2v(ra)); + setobj(L, uv->v.p, s2v(ra)); luaC_barrier(L, uv, s2v(ra)); vmbreak; } vmcase(OP_GETTABUP) { + StkId ra = RA(i); const TValue *slot; - TValue *upval = cl->upvals[GETARG_B(i)]->v; + TValue *upval = cl->upvals[GETARG_B(i)]->v.p; TValue *rc = KC(i); TString *key = tsvalue(rc); /* key must be a string */ if (luaV_fastget(L, upval, key, slot, luaH_getshortstr)) { @@ -1244,6 +1262,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_GETTABLE) { + StkId ra = RA(i); const TValue *slot; TValue *rb = vRB(i); TValue *rc = vRC(i); @@ -1258,6 +1277,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_GETI) { + StkId ra = RA(i); const TValue *slot; TValue *rb = vRB(i); int c = GETARG_C(i); @@ -1272,6 +1292,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_GETFIELD) { + StkId ra = RA(i); const TValue *slot; TValue *rb = vRB(i); TValue *rc = KC(i); @@ -1285,7 +1306,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { } vmcase(OP_SETTABUP) { const TValue *slot; - TValue *upval = cl->upvals[GETARG_A(i)]->v; + TValue *upval = cl->upvals[GETARG_A(i)]->v.p; TValue *rb = KB(i); TValue *rc = RKC(i); TString *key = tsvalue(rb); /* key must be a string */ @@ -1297,6 +1318,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_SETTABLE) { + StkId ra = RA(i); const TValue *slot; TValue *rb = vRB(i); /* key (table is in 'ra') */ TValue *rc = RKC(i); /* value */ @@ -1311,6 +1333,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_SETI) { + StkId ra = RA(i); const TValue *slot; int c = GETARG_B(i); TValue *rc = RKC(i); @@ -1325,6 +1348,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_SETFIELD) { + StkId ra = RA(i); const TValue *slot; TValue *rb = KB(i); TValue *rc = RKC(i); @@ -1337,6 +1361,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_NEWTABLE) { + StkId ra = RA(i); int b = GETARG_B(i); /* log2(hash size) + 1 */ int c = GETARG_C(i); /* array size */ Table *t; @@ -1346,7 +1371,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { if (TESTARG_k(i)) /* non-zero extra argument? */ c += GETARG_Ax(*pc) * (MAXARG_C + 1); /* add it to size */ pc++; /* skip extra argument */ - L->top = ra + 1; /* correct top in case of emergency GC */ + L->top.p = ra + 1; /* correct top in case of emergency GC */ t = luaH_new(L); /* memory allocation */ sethvalue2s(L, ra, t); if (b != 0 || c != 0) @@ -1355,6 +1380,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_SELF) { + StkId ra = RA(i); const TValue *slot; TValue *rb = vRB(i); TValue *rc = RKC(i); @@ -1384,6 +1410,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_MODK) { + savestate(L, ci); /* in case of division by 0 */ op_arithK(L, luaV_mod, luaV_modf); vmbreak; } @@ -1396,6 +1423,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_IDIVK) { + savestate(L, ci); /* in case of division by 0 */ op_arithK(L, luaV_idiv, luai_numidiv); vmbreak; } @@ -1412,6 +1440,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_SHRI) { + StkId ra = RA(i); TValue *rb = vRB(i); int ic = GETARG_sC(i); lua_Integer ib; @@ -1421,6 +1450,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_SHLI) { + StkId ra = RA(i); TValue *rb = vRB(i); int ic = GETARG_sC(i); lua_Integer ib; @@ -1442,6 +1472,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_MOD) { + savestate(L, ci); /* in case of division by 0 */ op_arith(L, luaV_mod, luaV_modf); vmbreak; } @@ -1454,6 +1485,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_IDIV) { /* floor division */ + savestate(L, ci); /* in case of division by 0 */ op_arith(L, luaV_idiv, luai_numidiv); vmbreak; } @@ -1478,6 +1510,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_MMBIN) { + StkId ra = RA(i); Instruction pi = *(pc - 2); /* original arith. expression */ TValue *rb = vRB(i); TMS tm = (TMS)GETARG_C(i); @@ -1487,6 +1520,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_MMBINI) { + StkId ra = RA(i); Instruction pi = *(pc - 2); /* original arith. expression */ int imm = GETARG_sB(i); TMS tm = (TMS)GETARG_C(i); @@ -1496,6 +1530,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_MMBINK) { + StkId ra = RA(i); Instruction pi = *(pc - 2); /* original arith. expression */ TValue *imm = KB(i); TMS tm = (TMS)GETARG_C(i); @@ -1505,6 +1540,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_UNM) { + StkId ra = RA(i); TValue *rb = vRB(i); lua_Number nb; if (ttisinteger(rb)) { @@ -1519,6 +1555,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_BNOT) { + StkId ra = RA(i); TValue *rb = vRB(i); lua_Integer ib; if (tointegerns(rb, &ib)) { @@ -1529,6 +1566,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_NOT) { + StkId ra = RA(i); TValue *rb = vRB(i); if (l_isfalse(rb)) setbtvalue(s2v(ra)); @@ -1537,21 +1575,25 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_LEN) { + StkId ra = RA(i); Protect(luaV_objlen(L, ra, vRB(i))); vmbreak; } vmcase(OP_CONCAT) { + StkId ra = RA(i); int n = GETARG_B(i); /* number of elements to concatenate */ - L->top = ra + n; /* mark the end of concat operands */ + L->top.p = ra + n; /* mark the end of concat operands */ ProtectNT(luaV_concat(L, n)); - checkGC(L, L->top); /* 'luaV_concat' ensures correct top */ + checkGC(L, L->top.p); /* 'luaV_concat' ensures correct top */ vmbreak; } vmcase(OP_CLOSE) { + StkId ra = RA(i); Protect(luaF_close(L, ra, LUA_OK, 1)); vmbreak; } vmcase(OP_TBC) { + StkId ra = RA(i); /* create new to-be-closed upvalue */ halfProtect(luaF_newtbcupval(L, ra)); vmbreak; @@ -1561,6 +1603,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_EQ) { + StkId ra = RA(i); int cond; TValue *rb = vRB(i); Protect(cond = luaV_equalobj(L, s2v(ra), rb)); @@ -1576,6 +1619,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_EQK) { + StkId ra = RA(i); TValue *rb = KB(i); /* basic types do not use '__eq'; we can use raw equality */ int cond = luaV_rawequalobj(s2v(ra), rb); @@ -1583,6 +1627,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_EQI) { + StkId ra = RA(i); int cond; int im = GETARG_sB(i); if (ttisinteger(s2v(ra))) @@ -1611,11 +1656,13 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_TEST) { + StkId ra = RA(i); int cond = !l_isfalse(s2v(ra)); docondjump(); vmbreak; } vmcase(OP_TESTSET) { + StkId ra = RA(i); TValue *rb = vRB(i); if (l_isfalse(rb) == GETARG_k(i)) pc++; @@ -1626,11 +1673,12 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_CALL) { + StkId ra = RA(i); CallInfo *newci; int b = GETARG_B(i); int nresults = GETARG_C(i) - 1; if (b != 0) /* fixed number of arguments? */ - L->top = ra + b; /* top signals number of arguments */ + L->top.p = ra + b; /* top signals number of arguments */ /* else previous instruction set top */ savepc(L); /* in case of errors */ if ((newci = luaD_precall(L, ra, nresults)) == NULL) @@ -1642,54 +1690,57 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_TAILCALL) { + StkId ra = RA(i); int b = GETARG_B(i); /* number of arguments + 1 (function) */ int n; /* number of results when calling a C function */ int nparams1 = GETARG_C(i); /* delta is virtual 'func' - real 'func' (vararg functions) */ int delta = (nparams1) ? ci->u.l.nextraargs + nparams1 : 0; if (b != 0) - L->top = ra + b; + L->top.p = ra + b; else /* previous instruction set top */ - b = cast_int(L->top - ra); + b = cast_int(L->top.p - ra); savepc(ci); /* several calls here can raise errors */ if (TESTARG_k(i)) { luaF_closeupval(L, base); /* close upvalues from current call */ - lua_assert(L->tbclist < base); /* no pending tbc variables */ - lua_assert(base == ci->func + 1); + lua_assert(L->tbclist.p < base); /* no pending tbc variables */ + lua_assert(base == ci->func.p + 1); } if ((n = luaD_pretailcall(L, ci, ra, b, delta)) < 0) /* Lua function? */ goto startfunc; /* execute the callee */ else { /* C function? */ - ci->func -= delta; /* restore 'func' (if vararg) */ + ci->func.p -= delta; /* restore 'func' (if vararg) */ luaD_poscall(L, ci, n); /* finish caller */ updatetrap(ci); /* 'luaD_poscall' can change hooks */ goto ret; /* caller returns after the tail call */ } } vmcase(OP_RETURN) { + StkId ra = RA(i); int n = GETARG_B(i) - 1; /* number of results */ int nparams1 = GETARG_C(i); if (n < 0) /* not fixed? */ - n = cast_int(L->top - ra); /* get what is available */ + n = cast_int(L->top.p - ra); /* get what is available */ savepc(ci); if (TESTARG_k(i)) { /* may there be open upvalues? */ ci->u2.nres = n; /* save number of returns */ - if (L->top < ci->top) - L->top = ci->top; + if (L->top.p < ci->top.p) + L->top.p = ci->top.p; luaF_close(L, base, CLOSEKTOP, 1); updatetrap(ci); updatestack(ci); } if (nparams1) /* vararg function? */ - ci->func -= ci->u.l.nextraargs + nparams1; - L->top = ra + n; /* set call for 'luaD_poscall' */ + ci->func.p -= ci->u.l.nextraargs + nparams1; + L->top.p = ra + n; /* set call for 'luaD_poscall' */ luaD_poscall(L, ci, n); updatetrap(ci); /* 'luaD_poscall' can change hooks */ goto ret; } vmcase(OP_RETURN0) { if (l_unlikely(L->hookmask)) { - L->top = ra; + StkId ra = RA(i); + L->top.p = ra; savepc(ci); luaD_poscall(L, ci, 0); /* no hurry... */ trap = 1; @@ -1697,15 +1748,16 @@ void luaV_execute (lua_State *L, CallInfo *ci) { else { /* do the 'poscall' here */ int nres; L->ci = ci->previous; /* back to caller */ - L->top = base - 1; + L->top.p = base - 1; for (nres = ci->nresults; l_unlikely(nres > 0); nres--) - setnilvalue(s2v(L->top++)); /* all results are nil */ + setnilvalue(s2v(L->top.p++)); /* all results are nil */ } goto ret; } vmcase(OP_RETURN1) { if (l_unlikely(L->hookmask)) { - L->top = ra + 1; + StkId ra = RA(i); + L->top.p = ra + 1; savepc(ci); luaD_poscall(L, ci, 1); /* no hurry... */ trap = 1; @@ -1714,12 +1766,13 @@ void luaV_execute (lua_State *L, CallInfo *ci) { int nres = ci->nresults; L->ci = ci->previous; /* back to caller */ if (nres == 0) - L->top = base - 1; /* asked for no results */ + L->top.p = base - 1; /* asked for no results */ else { + StkId ra = RA(i); setobjs2s(L, base - 1, ra); /* at least this result */ - L->top = base; + L->top.p = base; for (; l_unlikely(nres > 1); nres--) - setnilvalue(s2v(L->top++)); /* complete missing results */ + setnilvalue(s2v(L->top.p++)); /* complete missing results */ } } ret: /* return from a Lua function */ @@ -1731,6 +1784,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { } } vmcase(OP_FORLOOP) { + StkId ra = RA(i); if (ttisinteger(s2v(ra + 2))) { /* integer loop? */ lua_Unsigned count = l_castS2U(ivalue(s2v(ra + 1))); if (count > 0) { /* still more iterations? */ @@ -1749,12 +1803,14 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_FORPREP) { + StkId ra = RA(i); savestate(L, ci); /* in case of errors */ if (forprep(L, ra)) pc += GETARG_Bx(i) + 1; /* skip the loop */ vmbreak; } vmcase(OP_TFORPREP) { + StkId ra = RA(i); /* create to-be-closed upvalue (if needed) */ halfProtect(luaF_newtbcupval(L, ra + 3)); pc += GETARG_Bx(i); @@ -1763,7 +1819,8 @@ void luaV_execute (lua_State *L, CallInfo *ci) { goto l_tforcall; } vmcase(OP_TFORCALL) { - l_tforcall: + l_tforcall: { + StkId ra = RA(i); /* 'ra' has the iterator function, 'ra + 1' has the state, 'ra + 2' has the control variable, and 'ra + 3' has the to-be-closed variable. The call will use the stack after @@ -1771,29 +1828,31 @@ void luaV_execute (lua_State *L, CallInfo *ci) { */ /* push function, state, and control variable */ memcpy(ra + 4, ra, 3 * sizeof(*ra)); - L->top = ra + 4 + 3; + L->top.p = ra + 4 + 3; ProtectNT(luaD_call(L, ra + 4, GETARG_C(i))); /* do the call */ updatestack(ci); /* stack may have changed */ i = *(pc++); /* go to next instruction */ lua_assert(GET_OPCODE(i) == OP_TFORLOOP && ra == RA(i)); goto l_tforloop; - } + }} vmcase(OP_TFORLOOP) { - l_tforloop: + l_tforloop: { + StkId ra = RA(i); if (!ttisnil(s2v(ra + 4))) { /* continue loop? */ setobjs2s(L, ra + 2, ra + 4); /* save control variable */ pc -= GETARG_Bx(i); /* jump back */ } vmbreak; - } + }} vmcase(OP_SETLIST) { + StkId ra = RA(i); int n = GETARG_B(i); unsigned int last = GETARG_C(i); Table *h = hvalue(s2v(ra)); if (n == 0) - n = cast_int(L->top - ra) - 1; /* get up to the top */ + n = cast_int(L->top.p - ra) - 1; /* get up to the top */ else - L->top = ci->top; /* correct top in case of emergency GC */ + L->top.p = ci->top.p; /* correct top in case of emergency GC */ last += n; if (TESTARG_k(i)) { last += GETARG_Ax(*pc) * (MAXARG_C + 1); @@ -1810,12 +1869,14 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_CLOSURE) { + StkId ra = RA(i); Proto *p = cl->p->p[GETARG_Bx(i)]; halfProtect(pushclosure(L, p, cl->upvals, base, ra)); checkGC(L, ra + 1); vmbreak; } vmcase(OP_VARARG) { + StkId ra = RA(i); int n = GETARG_C(i) - 1; /* required results */ Protect(luaT_getvarargs(L, ci, ra, n)); vmbreak; diff --git a/src/lua/lvm.h b/src/lua/lvm.h index 1bc16f3a5..dba1ad277 100644 --- a/src/lua/lvm.h +++ b/src/lua/lvm.h @@ -110,6 +110,11 @@ typedef enum { luaC_barrierback(L, gcvalue(t), v); } +/* +** Shift right is the same as shift left with a negative 'y' +*/ +#define luaV_shiftr(x,y) luaV_shiftl(x,intop(-, 0, y)) + LUAI_FUNC int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2); From 866ca4b657d30236307c2d5275da35ff0fef83c4 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 30 Apr 2023 21:11:51 +0200 Subject: [PATCH 20/49] Apply Pi-hole Lua patches Signed-off-by: DL6ER --- patch/lua/0001-add-pihole-library.patch | 4 ++-- src/lua/linit.c | 3 +++ src/lua/lua.c | 18 ++++++++++++++++-- src/lua/luac.c | 4 +++- src/lua/lualib.h | 4 ++++ 5 files changed, 28 insertions(+), 5 deletions(-) diff --git a/patch/lua/0001-add-pihole-library.patch b/patch/lua/0001-add-pihole-library.patch index 30a0f592a..dc3885aa5 100644 --- a/patch/lua/0001-add-pihole-library.patch +++ b/patch/lua/0001-add-pihole-library.patch @@ -48,9 +48,9 @@ index 454ce12f..a363925c 100644 + ftl_lua_init(L); + /***************************************************/ + - if (!runargs(L, argv, script)) /* execute arguments -e and -l */ + if (!runargs(L, argv, optlim)) /* execute arguments -e and -l */ return 0; /* something failed */ - if (script < argc && /* execute main script (if there is one) */ + if (script > 0) { /* execute main script (if there is one) */ @@ -616,7 +622,9 @@ static int pmain (lua_State *L) { } diff --git a/src/lua/linit.c b/src/lua/linit.c index 69808f84f..9a5bcfdcc 100644 --- a/src/lua/linit.c +++ b/src/lua/linit.c @@ -50,6 +50,9 @@ static const luaL_Reg loadedlibs[] = { {LUA_MATHLIBNAME, luaopen_math}, {LUA_UTF8LIBNAME, luaopen_utf8}, {LUA_DBLIBNAME, luaopen_debug}, + /****** Pi-hole modification ******/ + {LUA_PIHOLELIBNAME, luaopen_pihole}, + /**********************************/ {NULL, NULL} }; diff --git a/src/lua/lua.c b/src/lua/lua.c index 0ff884545..f269c9973 100644 --- a/src/lua/lua.c +++ b/src/lua/lua.c @@ -20,6 +20,10 @@ #include "lauxlib.h" #include "lualib.h" +/** Pi-hole modification **/ +#include "ftl_lua.h" +/**************************/ + #if !defined(LUA_PROGNAME) #define LUA_PROGNAME "lua" @@ -211,7 +215,9 @@ static int dostring (lua_State *L, const char *s, const char *name) { /* ** Receives 'globname[=modname]' and runs 'globname = require(modname)'. */ -static int dolibrary (lua_State *L, char *globname) { +/************** Pi-hole modification ***************/ +int dolibrary (lua_State *L, char *globname) { +/***************************************************/ int status; char *modname = strchr(globname, '='); if (modname == NULL) /* no explicit name? */ @@ -639,6 +645,12 @@ static int pmain (lua_State *L) { if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */ return 0; /* error running LUA_INIT */ } + + /************** Pi-hole modification ***************/ + // Load and enable libraries bundled with Pi-hole + ftl_lua_init(L); + /***************************************************/ + if (!runargs(L, argv, optlim)) /* execute arguments -e and -l */ return 0; /* something failed */ if (script > 0) { /* execute main script (if there is one) */ @@ -659,7 +671,9 @@ static int pmain (lua_State *L) { } -int main (int argc, char **argv) { +/******* Pi-hole modification ********/ +int lua_main (int argc, char **argv) { +/*************************************/ int status, result; lua_State *L = luaL_newstate(); /* create state */ if (L == NULL) { diff --git a/src/lua/luac.c b/src/lua/luac.c index 5f4a141bd..139468cc6 100644 --- a/src/lua/luac.c +++ b/src/lua/luac.c @@ -193,7 +193,9 @@ static int pmain(lua_State* L) return 0; } -int main(int argc, char* argv[]) +/******* Pi-hole modification ********/ +int luac_main(int argc, char* argv[]) +/*************************************/ { lua_State* L; int i=doargs(argc,argv); diff --git a/src/lua/lualib.h b/src/lua/lualib.h index 262552907..a7cab4eb0 100644 --- a/src/lua/lualib.h +++ b/src/lua/lualib.h @@ -44,6 +44,10 @@ LUAMOD_API int (luaopen_debug) (lua_State *L); #define LUA_LOADLIBNAME "package" LUAMOD_API int (luaopen_package) (lua_State *L); +/************ Pi-hole modification *************/ +#define LUA_PIHOLELIBNAME "pihole" +LUAMOD_API int (luaopen_pihole) (lua_State *L); +/***********************************************/ /* open all previous libraries */ LUALIB_API void (luaL_openlibs) (lua_State *L); From 49bc744846c1529e2dde9cebb7d343bd179bf981 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 3 May 2023 21:53:30 +0200 Subject: [PATCH 21/49] Add header analysis also in tcp_key_recurse to fix an issue with wrong upstream servers being attributed to DNSSEC-related queries when multiple upstream servers are defined (e.g. conditional forwarding) Signed-off-by: DL6ER --- src/dnsmasq/forward.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/dnsmasq/forward.c b/src/dnsmasq/forward.c index 1f2eb2ddf..389a07551 100644 --- a/src/dnsmasq/forward.c +++ b/src/dnsmasq/forward.c @@ -2089,6 +2089,8 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si unsigned char *packet = NULL; struct dns_header *new_header = NULL; + FTL_header_analysis(header->hb4, 0, server, daemon->log_display_id); + while (1) { size_t m; From 7871ebe32bf4e873a0acef7981af6f23f26c88fe Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 4 May 2023 07:21:34 +0200 Subject: [PATCH 22/49] Add extra debugging output Signed-off-by: DL6ER --- src/config.c | 8 -------- src/dnsmasq/forward.c | 2 +- src/dnsmasq_interface.c | 7 ++++++- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/config.c b/src/config.c index eae5cdb8a..a00c31df0 100644 --- a/src/config.c +++ b/src/config.c @@ -837,9 +837,6 @@ static char *parse_FTLconf(FILE *fp, const char *key) // Go to beginning of file fseek(fp, 0L, SEEK_SET); - if(config.debug & DEBUG_EXTRA) - logg("initial: conflinebuffer = %p, keystr = %p, size = %zu", conflinebuffer, keystr, size); - // Set size to zero if conflinebuffer is not available here // This causes getline() to allocate memory for the buffer itself if(conflinebuffer == NULL && size != 0) @@ -848,11 +845,6 @@ static char *parse_FTLconf(FILE *fp, const char *key) errno = 0; while(getline(&conflinebuffer, &size, fp) != -1) { - if(config.debug & DEBUG_EXTRA) - { - logg("conflinebuffer = %p, keystr = %p, size = %zu", conflinebuffer, keystr, size); - logg(" while reading line \"%s\" looking for \"%s\"", conflinebuffer, keystr); - } // Check if memory allocation failed if(conflinebuffer == NULL) break; diff --git a/src/dnsmasq/forward.c b/src/dnsmasq/forward.c index 389a07551..ad01c1b94 100644 --- a/src/dnsmasq/forward.c +++ b/src/dnsmasq/forward.c @@ -2089,7 +2089,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si unsigned char *packet = NULL; struct dns_header *new_header = NULL; - FTL_header_analysis(header->hb4, 0, server, daemon->log_display_id); + FTL_header_analysis(header->hb4, RCODE(header), server, daemon->log_display_id); while (1) { diff --git a/src/dnsmasq_interface.c b/src/dnsmasq_interface.c index e18b48309..e73e745e3 100644 --- a/src/dnsmasq_interface.c +++ b/src/dnsmasq_interface.c @@ -2614,7 +2614,12 @@ void _FTL_header_analysis(const unsigned char header4, const unsigned int rcode, { memcpy(&last_server, &server->addr, sizeof(last_server)); if(config.debug & DEBUG_EXTRA) - logg("Got forward address: YES"); + { + char ip[ADDRSTRLEN+1] = { 0 }; + in_port_t port = 0; + mysockaddr_extract_ip_port(&last_server, ip, &port); + logg("Got forward address: %s#%u (%s:%i)", ip, port, short_path(file), line); + } } else { From a2746d70c51cd9958cac5b8fb13e64d9b8a83b64 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 4 May 2023 11:32:03 +0200 Subject: [PATCH 23/49] Also analyze UDP reply headers Signed-off-by: DL6ER --- src/dnsmasq/forward.c | 2 ++ src/dnsmasq_interface.c | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/src/dnsmasq/forward.c b/src/dnsmasq/forward.c index ad01c1b94..60e267640 100644 --- a/src/dnsmasq/forward.c +++ b/src/dnsmasq/forward.c @@ -1170,6 +1170,8 @@ void reply_query(int fd, time_t now) server = daemon->serverarray[c]; + FTL_header_analysis(header->hb4, RCODE(header), server, daemon->log_display_id); + if (RCODE(header) != REFUSED) daemon->serverarray[first]->last_server = c; else if (daemon->serverarray[first]->last_server == c) diff --git a/src/dnsmasq_interface.c b/src/dnsmasq_interface.c index e73e745e3..3a4be64a5 100644 --- a/src/dnsmasq_interface.c +++ b/src/dnsmasq_interface.c @@ -1877,6 +1877,8 @@ static void update_upstream(queriesData *query, const int id) id, oldaddr, oldport, ip, port); } } + + // Update upstream server ID query->upstreamID = upstreamID; } } @@ -2025,6 +2027,10 @@ static void FTL_reply(const unsigned int flags, const char *name, const union al if(!cached) update_upstream(query, id); + // Reset last_server to avoid possibly changing the upstream server + // again in the next query + memset(&last_server, 0, sizeof(last_server)); + // Save response time // Skipped internally if already computed set_response_time(query, response); @@ -2530,6 +2536,9 @@ static void FTL_upstream_error(const union all_addr *addr, const unsigned int fl // Set query reply query_set_reply(0, reply, addr, query, response); + // Reset last_server + memset(&last_server, 0, sizeof(last_server)); + // Unlock shared memory unlock_shm(); } From 51ca8f369b49df0051b5d41649448f9747defb41 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 7 May 2023 22:11:35 +0200 Subject: [PATCH 24/49] Do not log running out of disk space when the disk occupation is > 100%. We are seeing this with docker deployments on macOS hosts. It is a band-aid fix, however, it also seem to be the only thing we can do given that docker didn't fix this in nearly two years now. Signed-off-by: DL6ER --- src/gc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gc.c b/src/gc.c index 47734c110..0fbfd359d 100644 --- a/src/gc.c +++ b/src/gc.c @@ -86,9 +86,9 @@ static int check_space(const char *file, int LastUsage) // exceeds the configured threshold and current usage is higher than // usage in the last run (to prevent log spam) perc = get_filepath_usage(file, buffer); - if(perc > config.check.disk && perc > LastUsage ) + if(perc > config.check.disk && perc > LastUsage && perc <= 100.0) log_resource_shortage(-1.0, 0, -1, perc, file, buffer); - + return perc; } From b05ada45e9b9b0cc16219ee20cc7cea539e570aa Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 13 May 2023 12:40:39 +0200 Subject: [PATCH 25/49] Move dhcp-discover into a dedicated "tools" target Signed-off-by: DL6ER --- src/CMakeLists.txt | 4 ++-- src/args.c | 2 +- src/tools/CMakeLists.txt | 18 ++++++++++++++++++ src/{ => tools}/dhcp-discover.c | 0 src/{ => tools}/dhcp-discover.h | 0 5 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 src/tools/CMakeLists.txt rename src/{ => tools}/dhcp-discover.c (100%) rename src/{ => tools}/dhcp-discover.h (100%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e71381793..cee7b1b32 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -116,8 +116,6 @@ set(sources daemon.h datastructure.c datastructure.h - dhcp-discover.c - dhcp-discover.h dnsmasq_interface.c dnsmasq_interface.h edns0.c @@ -180,6 +178,7 @@ add_executable(pihole-FTL $ $ $ + $ ) if(STATIC STREQUAL "true") set_target_properties(pihole-FTL PROPERTIES LINK_SEARCH_START_STATIC ON) @@ -253,3 +252,4 @@ add_subdirectory(lua) add_subdirectory(lua/scripts) add_subdirectory(tre-regex) add_subdirectory(syscalls) +add_subdirectory(tools) diff --git a/src/args.c b/src/args.c index e5917f154..2df29b46e 100644 --- a/src/args.c +++ b/src/args.c @@ -33,7 +33,7 @@ // LUA dependencies #include "lua/ftl_lua.h" // run_dhcp_discover() -#include "dhcp-discover.h" +#include "tools/dhcp-discover.h" // defined in dnsmasq.c extern void print_dnsmasq_version(const char *yellow, const char *green, const char *bold, const char *normal); diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt new file mode 100644 index 000000000..d13a5942e --- /dev/null +++ b/src/tools/CMakeLists.txt @@ -0,0 +1,18 @@ +# Pi-hole: A black hole for Internet advertisements +# (c) 2020 Pi-hole, LLC (https://pi-hole.net) +# Network-wide ad blocking via your own hardware. +# +# FTL Engine +# /src/tools/CMakeList.txt +# +# This file is copyright under the latest version of the EUPL. +# Please see LICENSE file for your rights under this license. + +set(tools_sources + dhcp-discover.c + dhcp-discover.h + ) + +add_library(tools OBJECT ${tools_sources}) +target_compile_options(tools PRIVATE "${EXTRAWARN}") +target_include_directories(tools PRIVATE ${PROJECT_SOURCE_DIR}/src) diff --git a/src/dhcp-discover.c b/src/tools/dhcp-discover.c similarity index 100% rename from src/dhcp-discover.c rename to src/tools/dhcp-discover.c diff --git a/src/dhcp-discover.h b/src/tools/dhcp-discover.h similarity index 100% rename from src/dhcp-discover.h rename to src/tools/dhcp-discover.h From 29a33102f3b63272f86f54e49ce9957eeca973ad Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 13 May 2023 20:51:02 +0200 Subject: [PATCH 26/49] Add pihole-FTL arp-scan [{-v,-a}] Signed-off-by: DL6ER --- src/args.c | 37 ++- src/syscalls/recvfrom.c | 11 +- src/syscalls/sendto.c | 4 +- src/tools/CMakeLists.txt | 2 + src/tools/arp-scan.c | 486 ++++++++++++++++++++++++++++++++++++++ src/tools/arp-scan.h | 16 ++ src/tools/dhcp-discover.c | 2 +- src/tools/dhcp-discover.h | 1 + 8 files changed, 542 insertions(+), 17 deletions(-) create mode 100644 src/tools/arp-scan.c create mode 100644 src/tools/arp-scan.h diff --git a/src/args.c b/src/args.c index 2df29b46e..f86a19e30 100644 --- a/src/args.c +++ b/src/args.c @@ -34,6 +34,8 @@ #include "lua/ftl_lua.h" // run_dhcp_discover() #include "tools/dhcp-discover.h" +// run_arp_scan() +#include "tools/arp-scan.h" // defined in dnsmasq.c extern void print_dnsmasq_version(const char *yellow, const char *green, const char *bold, const char *normal); @@ -163,6 +165,24 @@ void parse_args(int argc, char* argv[]) (argc > 1 && strEndsWith(argv[1], ".db"))) exit(sqlite3_shell_main(argc, argv)); + // DHCP discovery mode + if(argc > 1 && strcmp(argv[1], "dhcp-discover") == 0) + { + // Enable stdout printing + cli_mode = true; + exit(run_dhcp_discover()); + } + + // ARP scanning mode + if(argc > 1 && strcmp(argv[1], "arp-scan") == 0) + { + // Enable stdout printing + cli_mode = true; + const bool verbose = argc > 2 && strcmp(argv[2], "-v") == 0; + const bool arp_all = argc > 2 && strcmp(argv[2], "-a") == 0; + exit(run_arp_scan(verbose, arp_all)); + } + // start from 1, as argv[0] is the executable name for(int i = 1; i < argc; i++) { @@ -415,14 +435,6 @@ void parse_args(int argc, char* argv[]) } } - // Regex test mode - if(strcmp(argv[i], "dhcp-discover") == 0) - { - // Enable stdout printing - cli_mode = true; - exit(run_dhcp_discover()); - } - // List of implemented arguments if(strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "help") == 0 || strcmp(argv[i], "--help") == 0) { @@ -495,13 +507,18 @@ void parse_args(int argc, char* argv[]) printf("%sDebugging and special use:%s\n", yellow, normal); printf("\t%sd%s, %sdebug%s Enter debugging mode\n", green, normal, green, normal); - printf("\t%stest%s Don't start pihole-FTL but\n", green, normal); - printf("\t instead quit immediately\n"); + printf("\t%stest%s Don't start pihole-FTL but instead\n", green, normal); + printf("\t quit immediately\n"); printf("\t%s-f%s, %sno-daemon%s Don't go into daemon mode\n\n", green, normal, green, normal); printf("%sOther:%s\n", yellow, normal); printf("\t%sdhcp-discover%s Discover DHCP servers in the local\n", green, normal); printf("\t network\n"); + printf("\t%sarp-scan %s[{-v/-a}]%s Use ARP to scan local network for\n", green, cyan, normal); + printf("\t possible IP conflicts\n"); + printf("\t Append %s-v%s for verbose output mode\n", cyan, normal); + printf("\t Append %s-a%s to force scan on all\n", cyan, normal); + printf("\t interfaces\n"); printf("\t%s-h%s, %shelp%s Display this help and exit\n\n", green, normal, green, normal); exit(EXIT_SUCCESS); } diff --git a/src/syscalls/recvfrom.c b/src/syscalls/recvfrom.c index 8db0ac468..4dff79285 100644 --- a/src/syscalls/recvfrom.c +++ b/src/syscalls/recvfrom.c @@ -32,11 +32,14 @@ ssize_t FTLrecvfrom(int sockfd, void *buf, size_t len, int flags, struct sockadd // Backup errno value const int _errno = errno; - // Final error checking (may have failed for some other reason then an - // EINTR = interrupted system call) - if(ret < 0) + // Final error checking. May have failed for some other reason then an + // EINTR = interrupted system call. In that case, log a warning However, + // if the error is EAGAIN, this is not an error, but just a non-blocking + // socket that has no data available or we ran into an (expected) + // timeout. In that case, do not log a warning + if(ret < 0 && errno != EAGAIN) logg("WARN: Could not recvfrom() in %s() (%s:%i): %s", - func, file, line, strerror(errno)); + func, file, line, strerror(errno)); // Restore errno value errno = _errno; diff --git a/src/syscalls/sendto.c b/src/syscalls/sendto.c index cc7234c94..846c571e7 100644 --- a/src/syscalls/sendto.c +++ b/src/syscalls/sendto.c @@ -33,8 +33,8 @@ ssize_t FTLsendto(int sockfd, void *buf, size_t len, int flags, const struct soc const int _errno = errno; // Final error checking (may have failed for some other reason then an - // EINTR = interrupted system call) - if(ret < 0) + // EINTR = interrupted system call), also ignore EPROTONOSUPPORT (ARP scanning) + if(ret < 0 && errno != EPROTONOSUPPORT) logg("WARN: Could not sendto() in %s() (%s:%i): %s", func, file, line, strerror(errno)); diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt index d13a5942e..47de88b1a 100644 --- a/src/tools/CMakeLists.txt +++ b/src/tools/CMakeLists.txt @@ -9,6 +9,8 @@ # Please see LICENSE file for your rights under this license. set(tools_sources + arp-scan.c + arp-scan.h dhcp-discover.c dhcp-discover.h ) diff --git a/src/tools/arp-scan.c b/src/tools/arp-scan.c new file mode 100644 index 000000000..63bbdc295 --- /dev/null +++ b/src/tools/arp-scan.c @@ -0,0 +1,486 @@ +/* Pi-hole: A black hole for Internet advertisements +* (c) 2023 Pi-hole, LLC (https://pi-hole.net) +* Network-wide ad blocking via your own hardware. +* +* FTL Engine +* ARP scanning routines +* +* This file is copyright under the latest version of the EUPL. +* Please see LICENSE file for your rights under this license. */ +// Inspired by https://stackoverflow.com/a/39287433 but heavily modified + +#include "FTL.h" +#include "arp-scan.h" +#include "log.h" +// get_hardware_address() +#include "dhcp-discover.h" + +#include +#include +#include +//htons etc +#include + +// How many threads do we spawn at maximum? +// This is also the limit for interfaces +// we scan for DHCP activity. +#define MAXTHREADS 32 +#define MAX_MACS 3 +#define NUM_SCANS 10 +#define ARP_TIMEOUT 1 + +// Global lock used by all threads +static pthread_mutex_t lock; +static bool arp_verbose = false; +static bool arp_all = false; + +#define PROTO_ARP 0x0806 +#define ETH2_HEADER_LEN 14 +#define HW_TYPE 1 +#define MAC_LENGTH 6 +#define IPV4_LENGTH 4 +#define ARP_REQUEST 0x01 +#define ARP_REPLY 0x02 +#define BUF_SIZE 60 + +#pragma pack(push, 1) + +// ARP header struct +// See https://en.wikipedia.org/wiki/Address_Resolution_Protocol#Packet_structure +struct arp_header { + unsigned short hardware_type; + unsigned short protocol_type; + unsigned char hardware_len; + unsigned char protocol_len; + unsigned short opcode; + unsigned char sender_mac[MAC_LENGTH]; + unsigned char sender_ip[IPV4_LENGTH]; + unsigned char target_mac[MAC_LENGTH]; + unsigned char target_ip[IPV4_LENGTH]; +}; +#pragma pack(pop) + +struct arp_result { + unsigned int replied[NUM_SCANS]; + unsigned char mac[MAX_MACS][MAC_LENGTH]; +}; + +// Sends multiple ARP who-has request on interface ifindex, using source mac src_mac and source ip src_ip. +// Interates over all IP addresses in the range of dst_ip/cidr. +static int send_arps(const int fd, const int ifindex, const char *iface, const unsigned char *src_mac, + struct in_addr *src_ip, struct in_addr dst_ip, const int dst_cidr) +{ + int err = -1; + unsigned char buffer[BUF_SIZE]; + memset(buffer, 0, sizeof(buffer)); + + // Construct the Ethernet header + struct sockaddr_ll socket_address; + socket_address.sll_family = AF_PACKET; + socket_address.sll_protocol = htons(ETH_P_ARP); + socket_address.sll_ifindex = ifindex; + socket_address.sll_hatype = htons(ARPHRD_ETHER); + socket_address.sll_pkttype = PACKET_BROADCAST; + socket_address.sll_halen = MAC_LENGTH; + socket_address.sll_addr[6] = 0; + socket_address.sll_addr[7] = 0; + + struct ethhdr *send_req = (struct ethhdr *) buffer; + struct arp_header *arp_req = (struct arp_header *) (buffer + ETH2_HEADER_LEN); + ssize_t ret; + + // Destination is the broadcast address + memset(send_req->h_dest, 0xff, MAC_LENGTH); + + // Target MAC is zero (we don't know it) + memset(arp_req->target_mac, 0x00, MAC_LENGTH); + + // Source MAC to our own MAC address + memcpy(send_req->h_source, src_mac, MAC_LENGTH); + memcpy(arp_req->sender_mac, src_mac, MAC_LENGTH); + memcpy(socket_address.sll_addr, src_mac, MAC_LENGTH); + + // Protocol type is ARP + send_req->h_proto = htons(ETH_P_ARP); + + // Create ARP request + arp_req->hardware_type = htons(HW_TYPE); + arp_req->protocol_type = htons(ETH_P_IP); + arp_req->hardware_len = MAC_LENGTH; + arp_req->protocol_len = IPV4_LENGTH; + arp_req->opcode = htons(ARP_REQUEST); + + // Copy IP address to arp_req + memcpy(arp_req->sender_ip, &src_ip->s_addr, sizeof(src_ip->s_addr)); + + // Loop over all possible IP addresses in the range dst_ip/cidr + // We start at 1 because the first IP address has already been set above + for(unsigned int i = 0; i < (1u << (32 - dst_cidr)); i++) + { + // Fill in target IP address + memcpy(arp_req->target_ip, &dst_ip.s_addr, sizeof(dst_ip.s_addr)); + +#ifdef DEBUG + printf("Sending ARP request for %s@%s\n", inet_ntoa(*dst_ip), iface); +#endif + + // Send ARP request + ret = sendto(fd, buffer, 42, 0, (struct sockaddr *) &socket_address, sizeof(socket_address)); + if (ret == -1) + { + if(errno != EPROTONOSUPPORT) + printf("Unable to send ARP request for %s@%s: %s\n", + inet_ntoa(dst_ip), iface, strerror(errno)); + goto out; + } + + // Increment IP address + dst_ip.s_addr = htonl(ntohl(dst_ip.s_addr) + 1); + } + + err = 0; +out: + return err; +} + +static int create_arp_socket(const int ifindex, const char *iface) +{ + // Create socket for ARP communications + const int arp_socket = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP)); + if(arp_socket < 0) + { + printf("Unable to create socket for ARP communications on interface %s: %s\n", iface, strerror(errno)); + return -1; + } + + // Bind socket to interface + struct sockaddr_ll sll; + memset(&sll, 0, sizeof(struct sockaddr_ll)); + sll.sll_family = AF_PACKET; + sll.sll_ifindex = ifindex; + if (bind(arp_socket, (struct sockaddr*) &sll, sizeof(struct sockaddr_ll)) < 0) + { + printf("Unable to bind socket for ARP communications on interface %s: %s\n", iface, strerror(errno)); + close(arp_socket); + return -1; + } + + // Set timeout + struct timeval tv; + tv.tv_sec = ARP_TIMEOUT; + tv.tv_usec = 0; + if (setsockopt(arp_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) + { + printf("Unable to set timeout for ARP communications on interface %s: %s\n", iface, strerror(errno)); + close(arp_socket); + return -1; + } + + return arp_socket; +} + +// Read all ARP responses +static ssize_t read_arp(const int fd, const char *iface, struct in_addr *dst_ip, + struct arp_result *result, const size_t result_len, const unsigned int scan_id) +{ + ssize_t ret = 0; + unsigned char buffer[BUF_SIZE]; + + // Read ARP responses + while(ret >= 0) + { + ret = recvfrom(fd, buffer, BUF_SIZE, 0, NULL, NULL); + if (ret == -1) + { + if(errno == EAGAIN) + { + // Timeout + ret = 0; + break; + } + + // Error + printf("recvfrom(): %s", strerror(errno)); + break; + } + struct ethhdr *rcv_resp = (struct ethhdr *) buffer; + struct arp_header *arp_resp = (struct arp_header *) (buffer + ETH2_HEADER_LEN); + if (ntohs(rcv_resp->h_proto) != PROTO_ARP) + { +#ifdef DEBUG + printf("Not an ARP packet"); +#endif + continue; + } + if (ntohs(arp_resp->opcode) != ARP_REPLY) + { +#ifdef DEBUG + printf("Not an ARP reply"); +#endif + continue; + } +#ifdef DEBUG + printf("received ARP len=%ld", ret); +#endif + struct in_addr sender_a; + memcpy(&sender_a.s_addr, arp_resp->sender_ip, sizeof(sender_a.s_addr)); + +#ifdef DEBUG + printf("%-16s %-20s\t%02x:%02x:%02x:%02x:%02x:%02x", + iface, inet_ntoa(sender_a), + arp_resp->sender_mac[0], + arp_resp->sender_mac[1], + arp_resp->sender_mac[2], + arp_resp->sender_mac[3], + arp_resp->sender_mac[4], + arp_resp->sender_mac[5]); +#endif + + // Check if we have already found this IP address + uint32_t i = ntohl(sender_a.s_addr) - ntohl(dst_ip->s_addr); + if(i >= result_len) + { + printf("Received IP address %s out of range\n", inet_ntoa(sender_a)); + continue; + } + + // Memorize that we have received a reply for this IP address + result[i].replied[scan_id]++; + + // Save MAC address + for(unsigned int j = 0; j < MAX_MACS; j++) + { + // Check if received MAC is already stored in result[i].mac[j] + if(memcmp(result[i].mac[j], arp_resp->sender_mac, MAC_LENGTH) == 0) + { + break; + } + // Check if result[i].mac[j] is all-zero + if(memcmp(result[i].mac[j], "\x00\x00\x00\x00\x00\x00", MAC_LENGTH) == 0) + { + // Copy MAC address to result[i].mac[j] + memcpy(result[i].mac[j], arp_resp->sender_mac, sizeof(arp_resp->sender_mac)); + break; + } + } + } + + return ret; +} + +// Convert netmask to CIDR +static int netmask_to_cidr(struct in_addr *addr) +{ + // Count the number of set bits in an unsigned integer + return __builtin_popcount(addr->s_addr); +} + +static void *arp_scan_iface(void *args) +{ + // Get interface details + struct ifaddrs *ifa = (struct ifaddrs*)args; + + // Get interface name + const char *iface = ifa->ifa_name; + + // Set interface name as thread name + prctl(PR_SET_NAME, iface, 0, 0, 0); + + // Get interface IPv4 address + struct sockaddr_in src_addr = { 0 }; + memcpy(&src_addr, ((struct ifaddrs*)args)->ifa_addr, sizeof(src_addr)); + char ipstr[INET_ADDRSTRLEN] = { 0 }; + inet_ntop(AF_INET, &src_addr.sin_addr, ipstr, INET_ADDRSTRLEN); + + // Get interface netmask + struct sockaddr_in mask = { 0 }; + memcpy(&mask, ((struct ifaddrs*)args)->ifa_netmask, sizeof(mask)); + // char netmask[INET_ADDRSTRLEN] = { 0 }; + // inet_ntop(AF_INET, &mask.sin_addr, netmask, INET_ADDRSTRLEN); + + // Convert subnet to CIDR + const int cidr = netmask_to_cidr(&mask.sin_addr); + + // Get interface index + const int ifindex = if_nametoindex(iface); + + // Scan only interfaces with CIDR >= 24 + if(cidr < 24 && !arp_all) + { + printf("Skipped interface %s (%s/%i)\n", iface, ipstr, cidr); + pthread_exit(NULL); + } + if(arp_verbose) + printf("Scanning interface %s (%s/%i)...\n", iface, ipstr, cidr); + + // Create socket for ARP communications + const int arp_socket = create_arp_socket(ifindex, iface); + + // Cannot create socket, likely a permission error + if(arp_socket < 0) + pthread_exit(NULL); + + // Get hardware address of client machine + unsigned char mac[16] = { 0 }; + get_hardware_address(arp_socket, iface, mac); + + // Define destination IP address by masking source IP with netmask + struct in_addr dst_addr = { 0 }; + dst_addr.s_addr = src_addr.sin_addr.s_addr & mask.sin_addr.s_addr; + + // Allocate memory for ARP response buffer + const size_t arp_result_len = 1 << (32 - cidr); + struct arp_result *result = calloc(arp_result_len, sizeof(struct arp_result)); + + for(unsigned int scan_id = 0; scan_id < NUM_SCANS; scan_id++) + { +#ifdef DEBUG + printf("Scanning interface %s (%s/%i) for the %i. time\n", iface, ipstr, cidr, scan_id + 1); +#endif + // Send ARP requests to all IPs in subnet + if(send_arps(arp_socket, ifindex, iface, mac, &src_addr.sin_addr, dst_addr, cidr) != 0) + break; + + // Read ARP responses + if(read_arp(arp_socket, iface, &dst_addr, result, arp_result_len, scan_id) != 0) + break; + } + + // Check if there are any results + unsigned int replies = 0; + for(unsigned int i = 0; i < arp_result_len; i++) + for(unsigned int j = 0; j < NUM_SCANS; j++) + replies += result[i].replied[j]; + + if(pthread_mutex_lock(&lock) != 0) + return NULL; + + if(replies == 0) + { + printf("No devices found on interface %s (%s/%i)\n", iface, ipstr, cidr); + goto arp_scan_iface_end; + } + + // Print results + printf("ARP scan on interface %s (%s/%i) finished\n", iface, ipstr, cidr); + printf("%-20s %-16s %-17s Reply matrix\n", "IP address", "Interface", "MAC address"); + for(unsigned int i = 0; i < arp_result_len; i++) + { + // Check if IP address replied + bool replied = false, multiple_replies = false; + for(unsigned int j = 0; j < NUM_SCANS; j++) + { + if(result[i].replied[j] > 0) + { + replied = true; + multiple_replies |= result[i].replied[j] > 1; + } + } + if(!replied) + continue; + + // Convert IP address to string + struct in_addr ip = { 0 }; + ip.s_addr = htonl(ntohl(dst_addr.s_addr) + i); + inet_ntop(AF_INET, &ip, ipstr, INET_ADDRSTRLEN); + + // Print MAC addresses + unsigned int j = 0; + for(j = 0; j < MAX_MACS; j++) + { + // Check if result[i].mac[j] is all-zero + if(memcmp(result[i].mac[j], "\x00\x00\x00\x00\x00\x00", 6) == 0) + break; + + // Print MAC address + printf("%-20s %-16s %02x:%02x:%02x:%02x:%02x:%02x ", + ipstr, iface, + result[i].mac[j][0], + result[i].mac[j][1], + result[i].mac[j][2], + result[i].mac[j][3], + result[i].mac[j][4], + result[i].mac[j][5]); + + for(unsigned int k = 0; k < NUM_SCANS; k++) + { + printf(" %s", result[i].replied[k] > 0 ? "X" : "-"); + } + putc('\n', stdout); + } + + // Print warning if multiple MAC addresses replied + if(j > 1) + printf("WARNING: Multiple MAC addresses replied as %s\n", ipstr); + if(multiple_replies) + printf("WARNING: Received multiple replies for %s\n", ipstr); + } + putc('\n', stdout); + +arp_scan_iface_end: + if(pthread_mutex_unlock(&lock) != 0) + return NULL; + + // Close socket + close(arp_socket); + pthread_exit(NULL); +} + +int run_arp_scan(const bool verbose, const bool scan_all) +{ + arp_verbose = verbose; + arp_all = scan_all; + puts("Discovering IPv4 hosts on the network using the Address Resolution Protocol (ARP)...\n"); + + // Get interface names for available interfaces on this machine + // and launch a thread for each one + pthread_t scanthread[MAXTHREADS]; + pthread_attr_t attr; + // Initialize thread attributes object with default attribute values + pthread_attr_init(&attr); + + // Create processing/logging lock + pthread_mutexattr_t lock_attr = {}; + // Initialize the lock attributes + pthread_mutexattr_init(&lock_attr); + // Initialize the lock + pthread_mutex_init(&lock, &lock_attr); + // Destroy the lock attributes since we're done with it + pthread_mutexattr_destroy(&lock_attr); + + struct ifaddrs *addrs, *tmp; + getifaddrs(&addrs); + tmp = addrs; + + // Loop until there are no more interfaces available + // or we reached the maximum number of threads + int tid = 0; + while(tmp != NULL && tid < MAXTHREADS) + { + // Create a thread for interfaces of type AF_INET + if(tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_INET) + { + if(pthread_create(&scanthread[tid], &attr, arp_scan_iface, tmp ) != 0) + { + printf("Unable to launch thread for interface %s, skipping...\n", + tmp->ifa_name); + continue; + } + + // Increase thread ID + tid++; + } + + // Advance to the next interface + tmp = tmp->ifa_next; + } + + // Wait for all threads to join back with us + for(tid--; tid > -1; tid--) + pthread_join(scanthread[tid], NULL); + + // Free linked-list of interfaces on this client + freeifaddrs(addrs); + + return EXIT_SUCCESS; +} diff --git a/src/tools/arp-scan.h b/src/tools/arp-scan.h new file mode 100644 index 000000000..90b5fec70 --- /dev/null +++ b/src/tools/arp-scan.h @@ -0,0 +1,16 @@ +/* Pi-hole: A black hole for Internet advertisements +* (c) 2023 Pi-hole, LLC (https://pi-hole.net) +* Network-wide ad blocking via your own hardware. +* +* FTL Engine +* ARP scanning prototypes +* +* This file is copyright under the latest version of the EUPL. +* Please see LICENSE file for your rights under this license. */ + +#ifndef ARP_SCAN_H +#define ARP_SCAN_H + +int run_arp_scan(const bool verbose, const bool scan_all); + +#endif // ARP_SCAN_H diff --git a/src/tools/dhcp-discover.c b/src/tools/dhcp-discover.c index 547135525..5e2613eaa 100644 --- a/src/tools/dhcp-discover.c +++ b/src/tools/dhcp-discover.c @@ -128,7 +128,7 @@ static int create_dhcp_socket(const char *iname) } // determines hardware address on client machine -static int get_hardware_address(const int sock, const char *iname, unsigned char *mac) +int get_hardware_address(const int sock, const char *iname, unsigned char *mac) { struct ifreq ifr; strncpy((char *)&ifr.ifr_name, iname, sizeof(ifr.ifr_name)-1); diff --git a/src/tools/dhcp-discover.h b/src/tools/dhcp-discover.h index 7d9bc7e84..efd07c116 100644 --- a/src/tools/dhcp-discover.h +++ b/src/tools/dhcp-discover.h @@ -12,5 +12,6 @@ #define DHCP_DISCOVER_H int run_dhcp_discover(void); +int get_hardware_address(const int sock, const char *iname, unsigned char *mac); #endif // DHCP_DISCOVER_H From 36bd2a56e4e804c82332caca8d30f354a1106fd5 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 13 May 2023 22:22:17 +0200 Subject: [PATCH 27/49] Unify warning Signed-off-by: DL6ER --- src/tools/arp-scan.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/tools/arp-scan.c b/src/tools/arp-scan.c index 63bbdc295..cf47cf6cf 100644 --- a/src/tools/arp-scan.c +++ b/src/tools/arp-scan.c @@ -66,7 +66,7 @@ struct arp_result { }; // Sends multiple ARP who-has request on interface ifindex, using source mac src_mac and source ip src_ip. -// Interates over all IP addresses in the range of dst_ip/cidr. +// Iterates over all IP addresses in the range of dst_ip/cidr. static int send_arps(const int fd, const int ifindex, const char *iface, const unsigned char *src_mac, struct in_addr *src_ip, struct in_addr dst_ip, const int dst_cidr) { @@ -409,10 +409,8 @@ static void *arp_scan_iface(void *args) putc('\n', stdout); } - // Print warning if multiple MAC addresses replied - if(j > 1) - printf("WARNING: Multiple MAC addresses replied as %s\n", ipstr); - if(multiple_replies) + // Print warning if we received multiple replies + if(j > 1 || multiple_replies) printf("WARNING: Received multiple replies for %s\n", ipstr); } putc('\n', stdout); From 26396bd20b9a77b788e695b874c93172b4ec35e1 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 14 May 2023 07:26:06 +0200 Subject: [PATCH 28/49] Use dedicated counters per MAC for a more accurate per-device reply matrix Signed-off-by: DL6ER --- src/tools/arp-scan.c | 86 +++++++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 40 deletions(-) diff --git a/src/tools/arp-scan.c b/src/tools/arp-scan.c index cf47cf6cf..09aace8c4 100644 --- a/src/tools/arp-scan.c +++ b/src/tools/arp-scan.c @@ -61,8 +61,10 @@ struct arp_header { #pragma pack(pop) struct arp_result { - unsigned int replied[NUM_SCANS]; - unsigned char mac[MAX_MACS][MAC_LENGTH]; + struct device { + unsigned int replied[NUM_SCANS]; + unsigned char mac[MAC_LENGTH]; + } device[MAX_MACS]; }; // Sends multiple ARP who-has request on interface ifindex, using source mac src_mac and source ip src_ip. @@ -244,30 +246,32 @@ static ssize_t read_arp(const int fd, const char *iface, struct in_addr *dst_ip, continue; } - // Memorize that we have received a reply for this IP address - result[i].replied[scan_id]++; - // Save MAC address - for(unsigned int j = 0; j < MAX_MACS; j++) + unsigned int j = 0; + for(; j < MAX_MACS; j++) { - // Check if received MAC is already stored in result[i].mac[j] - if(memcmp(result[i].mac[j], arp_resp->sender_mac, MAC_LENGTH) == 0) + // Check if received MAC is already stored in result[i].device[j].mac + if(memcmp(result[i].device[j].mac, arp_resp->sender_mac, MAC_LENGTH) == 0) { break; } - // Check if result[i].mac[j] is all-zero - if(memcmp(result[i].mac[j], "\x00\x00\x00\x00\x00\x00", MAC_LENGTH) == 0) + // Check if result[i].device[j].mac is all-zero + if(memcmp(result[i].device[j].mac, "\x00\x00\x00\x00\x00\x00", MAC_LENGTH) == 0) { - // Copy MAC address to result[i].mac[j] - memcpy(result[i].mac[j], arp_resp->sender_mac, sizeof(arp_resp->sender_mac)); + // Copy MAC address to result[i].device[j].mac + memcpy(result[i].device[j].mac, arp_resp->sender_mac, sizeof(arp_resp->sender_mac)); break; } } + + // Memorize that we have received a reply for this IP address + result[i].device[j].replied[scan_id]++; } return ret; } + // Convert netmask to CIDR static int netmask_to_cidr(struct in_addr *addr) { @@ -349,8 +353,9 @@ static void *arp_scan_iface(void *args) // Check if there are any results unsigned int replies = 0; for(unsigned int i = 0; i < arp_result_len; i++) - for(unsigned int j = 0; j < NUM_SCANS; j++) - replies += result[i].replied[j]; + for(unsigned int j = 0; j < MAX_MACS; j++) + for(unsigned int k = 0; k < NUM_SCANS; k++) + replies += result[i].device[j].replied[k]; if(pthread_mutex_lock(&lock) != 0) return NULL; @@ -366,51 +371,52 @@ static void *arp_scan_iface(void *args) printf("%-20s %-16s %-17s Reply matrix\n", "IP address", "Interface", "MAC address"); for(unsigned int i = 0; i < arp_result_len; i++) { - // Check if IP address replied - bool replied = false, multiple_replies = false; - for(unsigned int j = 0; j < NUM_SCANS; j++) + unsigned int j = 0, replied_devices = 0; + bool multiple_replies = false; + + // Print MAC addresses + for(j = 0; j < MAX_MACS; j++) { - if(result[i].replied[j] > 0) + // Check if IP address replied + bool replied = false; + for(unsigned int k = 0; k < NUM_SCANS; k++) { - replied = true; - multiple_replies |= result[i].replied[j] > 1; + replied |= result[i].device[j].replied[k] > 0; + multiple_replies |= result[i].device[j].replied[k] > 1; } - } - if(!replied) - continue; + if(!replied) + continue; - // Convert IP address to string - struct in_addr ip = { 0 }; - ip.s_addr = htonl(ntohl(dst_addr.s_addr) + i); - inet_ntop(AF_INET, &ip, ipstr, INET_ADDRSTRLEN); + // Check if IP address replied multiple times from different MAC address + replied_devices++; - // Print MAC addresses - unsigned int j = 0; - for(j = 0; j < MAX_MACS; j++) - { + // Convert IP address to string + struct in_addr ip = { 0 }; + ip.s_addr = htonl(ntohl(dst_addr.s_addr) + i); + inet_ntop(AF_INET, &ip, ipstr, INET_ADDRSTRLEN); // Check if result[i].mac[j] is all-zero - if(memcmp(result[i].mac[j], "\x00\x00\x00\x00\x00\x00", 6) == 0) + if(memcmp(result[i].device[j].mac, "\x00\x00\x00\x00\x00\x00", 6) == 0) break; // Print MAC address printf("%-20s %-16s %02x:%02x:%02x:%02x:%02x:%02x ", ipstr, iface, - result[i].mac[j][0], - result[i].mac[j][1], - result[i].mac[j][2], - result[i].mac[j][3], - result[i].mac[j][4], - result[i].mac[j][5]); + result[i].device[j].mac[0], + result[i].device[j].mac[1], + result[i].device[j].mac[2], + result[i].device[j].mac[3], + result[i].device[j].mac[4], + result[i].device[j].mac[5]); for(unsigned int k = 0; k < NUM_SCANS; k++) { - printf(" %s", result[i].replied[k] > 0 ? "X" : "-"); + printf(" %s", result[i].device[j].replied[k] > 0 ? "X" : "-"); } putc('\n', stdout); } // Print warning if we received multiple replies - if(j > 1 || multiple_replies) + if(replied_devices > 1 || multiple_replies) printf("WARNING: Received multiple replies for %s\n", ipstr); } putc('\n', stdout); From 65d5b121278cb57972f13c483f3a0a8f5b61b181 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 14 May 2023 07:34:18 +0200 Subject: [PATCH 29/49] Add our own address to the scan results so we can detect IP conflicts also here Signed-off-by: DL6ER --- src/tools/arp-scan.c | 75 ++++++++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 31 deletions(-) diff --git a/src/tools/arp-scan.c b/src/tools/arp-scan.c index 09aace8c4..76430002f 100644 --- a/src/tools/arp-scan.c +++ b/src/tools/arp-scan.c @@ -181,6 +181,40 @@ static int create_arp_socket(const int ifindex, const char *iface) return arp_socket; } +static void add_result(const char *iface, struct in_addr *rcv_ip, struct in_addr *dst_ip, unsigned char *sender_mac, + struct arp_result *result, const size_t result_len, const unsigned int scan_id) +{ + + // Check if we have already found this IP address + uint32_t i = ntohl(rcv_ip->s_addr) - ntohl(dst_ip->s_addr); + if(i >= result_len) + { + printf("Received IP address %s out of range\n", inet_ntoa(*rcv_ip)); + return; + } + + // Save MAC address + unsigned int j = 0; + for(; j < MAX_MACS; j++) + { + // Check if received MAC is already stored in result[i].device[j].mac + if(memcmp(result[i].device[j].mac, sender_mac, MAC_LENGTH) == 0) + { + break; + } + // Check if result[i].device[j].mac is all-zero + if(memcmp(result[i].device[j].mac, "\x00\x00\x00\x00\x00\x00", MAC_LENGTH) == 0) + { + // Copy MAC address to result[i].device[j].mac + memcpy(result[i].device[j].mac, sender_mac, MAC_LENGTH); + break; + } + } + + // Memorize that we have received a reply for this IP address + result[i].device[j].replied[scan_id]++; +} + // Read all ARP responses static ssize_t read_arp(const int fd, const char *iface, struct in_addr *dst_ip, struct arp_result *result, const size_t result_len, const unsigned int scan_id) @@ -237,41 +271,12 @@ static ssize_t read_arp(const int fd, const char *iface, struct in_addr *dst_ip, arp_resp->sender_mac[4], arp_resp->sender_mac[5]); #endif - - // Check if we have already found this IP address - uint32_t i = ntohl(sender_a.s_addr) - ntohl(dst_ip->s_addr); - if(i >= result_len) - { - printf("Received IP address %s out of range\n", inet_ntoa(sender_a)); - continue; - } - - // Save MAC address - unsigned int j = 0; - for(; j < MAX_MACS; j++) - { - // Check if received MAC is already stored in result[i].device[j].mac - if(memcmp(result[i].device[j].mac, arp_resp->sender_mac, MAC_LENGTH) == 0) - { - break; - } - // Check if result[i].device[j].mac is all-zero - if(memcmp(result[i].device[j].mac, "\x00\x00\x00\x00\x00\x00", MAC_LENGTH) == 0) - { - // Copy MAC address to result[i].device[j].mac - memcpy(result[i].device[j].mac, arp_resp->sender_mac, sizeof(arp_resp->sender_mac)); - break; - } - } - - // Memorize that we have received a reply for this IP address - result[i].device[j].replied[scan_id]++; + add_result(iface, &sender_a, dst_ip, arp_resp->sender_mac, result, result_len, scan_id); } return ret; } - // Convert netmask to CIDR static int netmask_to_cidr(struct in_addr *addr) { @@ -360,15 +365,23 @@ static void *arp_scan_iface(void *args) if(pthread_mutex_lock(&lock) != 0) return NULL; + // Exit early if there are no results if(replies == 0) { printf("No devices found on interface %s (%s/%i)\n", iface, ipstr, cidr); goto arp_scan_iface_end; } - // Print results + // If there is at least one result, print header printf("ARP scan on interface %s (%s/%i) finished\n", iface, ipstr, cidr); printf("%-20s %-16s %-17s Reply matrix\n", "IP address", "Interface", "MAC address"); + + // Add our own IP address to the results so IP conflicts can be detected + // (our own IP address is not included in the ARP scan) + for(unsigned int i = 0; i < NUM_SCANS; i++) + add_result(iface, &src_addr.sin_addr, &dst_addr, mac, result, arp_result_len, i); + + // Print results for(unsigned int i = 0; i < arp_result_len; i++) { unsigned int j = 0, replied_devices = 0; From 0e74485c15dfb5d2b60203e2286ff308c0373a88 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 14 May 2023 07:49:42 +0200 Subject: [PATCH 30/49] Include hostnames (if available) Signed-off-by: DL6ER --- src/tools/arp-scan.c | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/tools/arp-scan.c b/src/tools/arp-scan.c index 76430002f..e549cfa2c 100644 --- a/src/tools/arp-scan.c +++ b/src/tools/arp-scan.c @@ -25,8 +25,14 @@ // This is also the limit for interfaces // we scan for DHCP activity. #define MAXTHREADS 32 + +// How many MAC addresses do we store per IP address? #define MAX_MACS 3 + +// How many ARP requests do we send per IP address? #define NUM_SCANS 10 + +// How long do we wait for ARP replies in each scan [seconds]? #define ARP_TIMEOUT 1 // Global lock used by all threads @@ -34,6 +40,7 @@ static pthread_mutex_t lock; static bool arp_verbose = false; static bool arp_all = false; +// Protocol definitions #define PROTO_ARP 0x0806 #define ETH2_HEADER_LEN 14 #define HW_TYPE 1 @@ -43,10 +50,9 @@ static bool arp_all = false; #define ARP_REPLY 0x02 #define BUF_SIZE 60 -#pragma pack(push, 1) - // ARP header struct // See https://en.wikipedia.org/wiki/Address_Resolution_Protocol#Packet_structure +#pragma pack(push, 1) struct arp_header { unsigned short hardware_type; unsigned short protocol_type; @@ -284,6 +290,21 @@ static int netmask_to_cidr(struct in_addr *addr) return __builtin_popcount(addr->s_addr); } +static const char *get_hostname(const struct in_addr *addr) +{ + // Get hostname + struct hostent *he = gethostbyaddr(&addr->s_addr, sizeof(addr->s_addr), AF_INET); + if(he == NULL) + return "N/A"; + + // Allow at most 24 characters for the hostname + static char hostname[25] = { 0 }; + strncpy(hostname, he->h_name, 24); + + // Return hostname + return hostname; +} + static void *arp_scan_iface(void *args) { // Get interface details @@ -374,7 +395,7 @@ static void *arp_scan_iface(void *args) // If there is at least one result, print header printf("ARP scan on interface %s (%s/%i) finished\n", iface, ipstr, cidr); - printf("%-20s %-16s %-17s Reply matrix\n", "IP address", "Interface", "MAC address"); + printf("%-16s %-10s %-24s %-17s Reply matrix\n", "IP address", "Interface", "Hostname", "MAC address"); // Add our own IP address to the results so IP conflicts can be detected // (our own IP address is not included in the ARP scan) @@ -412,8 +433,8 @@ static void *arp_scan_iface(void *args) break; // Print MAC address - printf("%-20s %-16s %02x:%02x:%02x:%02x:%02x:%02x ", - ipstr, iface, + printf("%-16s %-10s %-24s %02x:%02x:%02x:%02x:%02x:%02x ", + ipstr, iface, get_hostname(&ip), result[i].device[j].mac[0], result[i].device[j].mac[1], result[i].device[j].mac[2], @@ -422,9 +443,8 @@ static void *arp_scan_iface(void *args) result[i].device[j].mac[5]); for(unsigned int k = 0; k < NUM_SCANS; k++) - { printf(" %s", result[i].device[j].replied[k] > 0 ? "X" : "-"); - } + putc('\n', stdout); } From d6695ebd4259953470017aa4d5071f31231cae54 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 14 May 2023 09:10:34 +0200 Subject: [PATCH 31/49] Update embedded Lua to 5.4.6 Signed-off-by: DL6ER --- src/lua/lcorolib.c | 4 ++-- src/lua/linit.c | 3 --- src/lua/lstate.c | 10 +++++++++- src/lua/lua.c | 18 ++---------------- src/lua/lua.h | 7 ++++--- src/lua/luac.c | 4 +--- src/lua/lualib.h | 4 ---- 7 files changed, 18 insertions(+), 32 deletions(-) diff --git a/src/lua/lcorolib.c b/src/lua/lcorolib.c index 40b880b14..c64adf08a 100644 --- a/src/lua/lcorolib.c +++ b/src/lua/lcorolib.c @@ -76,7 +76,7 @@ static int luaB_auxwrap (lua_State *L) { if (l_unlikely(r < 0)) { /* error? */ int stat = lua_status(co); if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */ - stat = lua_resetthread(co, L); /* close its tbc variables */ + stat = lua_closethread(co, L); /* close its tbc variables */ lua_assert(stat != LUA_OK); lua_xmove(co, L, 1); /* move error message to the caller */ } @@ -172,7 +172,7 @@ static int luaB_close (lua_State *L) { int status = auxstatus(L, co); switch (status) { case COS_DEAD: case COS_YIELD: { - status = lua_resetthread(co, L); + status = lua_closethread(co, L); if (status == LUA_OK) { lua_pushboolean(L, 1); return 1; diff --git a/src/lua/linit.c b/src/lua/linit.c index 9a5bcfdcc..69808f84f 100644 --- a/src/lua/linit.c +++ b/src/lua/linit.c @@ -50,9 +50,6 @@ static const luaL_Reg loadedlibs[] = { {LUA_MATHLIBNAME, luaopen_math}, {LUA_UTF8LIBNAME, luaopen_utf8}, {LUA_DBLIBNAME, luaopen_debug}, - /****** Pi-hole modification ******/ - {LUA_PIHOLELIBNAME, luaopen_pihole}, - /**********************************/ {NULL, NULL} }; diff --git a/src/lua/lstate.c b/src/lua/lstate.c index 1fbefb4b1..1e925e5ad 100644 --- a/src/lua/lstate.c +++ b/src/lua/lstate.c @@ -339,7 +339,7 @@ int luaE_resetthread (lua_State *L, int status) { } -LUA_API int lua_resetthread (lua_State *L, lua_State *from) { +LUA_API int lua_closethread (lua_State *L, lua_State *from) { int status; lua_lock(L); L->nCcalls = (from) ? getCcalls(from) : 0; @@ -349,6 +349,14 @@ LUA_API int lua_resetthread (lua_State *L, lua_State *from) { } +/* +** Deprecated! Use 'lua_closethread' instead. +*/ +LUA_API int lua_resetthread (lua_State *L) { + return lua_closethread(L, NULL); +} + + LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { int i; lua_State *L; diff --git a/src/lua/lua.c b/src/lua/lua.c index f269c9973..0ff884545 100644 --- a/src/lua/lua.c +++ b/src/lua/lua.c @@ -20,10 +20,6 @@ #include "lauxlib.h" #include "lualib.h" -/** Pi-hole modification **/ -#include "ftl_lua.h" -/**************************/ - #if !defined(LUA_PROGNAME) #define LUA_PROGNAME "lua" @@ -215,9 +211,7 @@ static int dostring (lua_State *L, const char *s, const char *name) { /* ** Receives 'globname[=modname]' and runs 'globname = require(modname)'. */ -/************** Pi-hole modification ***************/ -int dolibrary (lua_State *L, char *globname) { -/***************************************************/ +static int dolibrary (lua_State *L, char *globname) { int status; char *modname = strchr(globname, '='); if (modname == NULL) /* no explicit name? */ @@ -645,12 +639,6 @@ static int pmain (lua_State *L) { if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */ return 0; /* error running LUA_INIT */ } - - /************** Pi-hole modification ***************/ - // Load and enable libraries bundled with Pi-hole - ftl_lua_init(L); - /***************************************************/ - if (!runargs(L, argv, optlim)) /* execute arguments -e and -l */ return 0; /* something failed */ if (script > 0) { /* execute main script (if there is one) */ @@ -671,9 +659,7 @@ static int pmain (lua_State *L) { } -/******* Pi-hole modification ********/ -int lua_main (int argc, char **argv) { -/*************************************/ +int main (int argc, char **argv) { int status, result; lua_State *L = luaL_newstate(); /* create state */ if (L == NULL) { diff --git a/src/lua/lua.h b/src/lua/lua.h index 01927c6d9..fd16cf805 100644 --- a/src/lua/lua.h +++ b/src/lua/lua.h @@ -18,10 +18,10 @@ #define LUA_VERSION_MAJOR "5" #define LUA_VERSION_MINOR "4" -#define LUA_VERSION_RELEASE "5" +#define LUA_VERSION_RELEASE "6" #define LUA_VERSION_NUM 504 -#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 5) +#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 6) #define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR #define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE @@ -163,7 +163,8 @@ extern const char lua_ident[]; 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 int (lua_resetthread) (lua_State *L, lua_State *from); +LUA_API int (lua_closethread) (lua_State *L, lua_State *from); +LUA_API int (lua_resetthread) (lua_State *L); /* Deprecated! */ LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf); diff --git a/src/lua/luac.c b/src/lua/luac.c index 139468cc6..5f4a141bd 100644 --- a/src/lua/luac.c +++ b/src/lua/luac.c @@ -193,9 +193,7 @@ static int pmain(lua_State* L) return 0; } -/******* Pi-hole modification ********/ -int luac_main(int argc, char* argv[]) -/*************************************/ +int main(int argc, char* argv[]) { lua_State* L; int i=doargs(argc,argv); diff --git a/src/lua/lualib.h b/src/lua/lualib.h index a7cab4eb0..262552907 100644 --- a/src/lua/lualib.h +++ b/src/lua/lualib.h @@ -44,10 +44,6 @@ LUAMOD_API int (luaopen_debug) (lua_State *L); #define LUA_LOADLIBNAME "package" LUAMOD_API int (luaopen_package) (lua_State *L); -/************ Pi-hole modification *************/ -#define LUA_PIHOLELIBNAME "pihole" -LUAMOD_API int (luaopen_pihole) (lua_State *L); -/***********************************************/ /* open all previous libraries */ LUALIB_API void (luaL_openlibs) (lua_State *L); From ec72408cc33a6ece90002120409fe3ceb6119688 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 14 May 2023 09:11:02 +0200 Subject: [PATCH 32/49] Re-apply Pi-hole specific Lua patches Signed-off-by: DL6ER --- src/lua/linit.c | 3 +++ src/lua/lua.c | 18 ++++++++++++++++-- src/lua/luac.c | 4 +++- src/lua/lualib.h | 4 ++++ 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/lua/linit.c b/src/lua/linit.c index 69808f84f..9a5bcfdcc 100644 --- a/src/lua/linit.c +++ b/src/lua/linit.c @@ -50,6 +50,9 @@ static const luaL_Reg loadedlibs[] = { {LUA_MATHLIBNAME, luaopen_math}, {LUA_UTF8LIBNAME, luaopen_utf8}, {LUA_DBLIBNAME, luaopen_debug}, + /****** Pi-hole modification ******/ + {LUA_PIHOLELIBNAME, luaopen_pihole}, + /**********************************/ {NULL, NULL} }; diff --git a/src/lua/lua.c b/src/lua/lua.c index 0ff884545..f269c9973 100644 --- a/src/lua/lua.c +++ b/src/lua/lua.c @@ -20,6 +20,10 @@ #include "lauxlib.h" #include "lualib.h" +/** Pi-hole modification **/ +#include "ftl_lua.h" +/**************************/ + #if !defined(LUA_PROGNAME) #define LUA_PROGNAME "lua" @@ -211,7 +215,9 @@ static int dostring (lua_State *L, const char *s, const char *name) { /* ** Receives 'globname[=modname]' and runs 'globname = require(modname)'. */ -static int dolibrary (lua_State *L, char *globname) { +/************** Pi-hole modification ***************/ +int dolibrary (lua_State *L, char *globname) { +/***************************************************/ int status; char *modname = strchr(globname, '='); if (modname == NULL) /* no explicit name? */ @@ -639,6 +645,12 @@ static int pmain (lua_State *L) { if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */ return 0; /* error running LUA_INIT */ } + + /************** Pi-hole modification ***************/ + // Load and enable libraries bundled with Pi-hole + ftl_lua_init(L); + /***************************************************/ + if (!runargs(L, argv, optlim)) /* execute arguments -e and -l */ return 0; /* something failed */ if (script > 0) { /* execute main script (if there is one) */ @@ -659,7 +671,9 @@ static int pmain (lua_State *L) { } -int main (int argc, char **argv) { +/******* Pi-hole modification ********/ +int lua_main (int argc, char **argv) { +/*************************************/ int status, result; lua_State *L = luaL_newstate(); /* create state */ if (L == NULL) { diff --git a/src/lua/luac.c b/src/lua/luac.c index 5f4a141bd..139468cc6 100644 --- a/src/lua/luac.c +++ b/src/lua/luac.c @@ -193,7 +193,9 @@ static int pmain(lua_State* L) return 0; } -int main(int argc, char* argv[]) +/******* Pi-hole modification ********/ +int luac_main(int argc, char* argv[]) +/*************************************/ { lua_State* L; int i=doargs(argc,argv); diff --git a/src/lua/lualib.h b/src/lua/lualib.h index 262552907..a7cab4eb0 100644 --- a/src/lua/lualib.h +++ b/src/lua/lualib.h @@ -44,6 +44,10 @@ LUAMOD_API int (luaopen_debug) (lua_State *L); #define LUA_LOADLIBNAME "package" LUAMOD_API int (luaopen_package) (lua_State *L); +/************ Pi-hole modification *************/ +#define LUA_PIHOLELIBNAME "pihole" +LUAMOD_API int (luaopen_pihole) (lua_State *L); +/***********************************************/ /* open all previous libraries */ LUALIB_API void (luaL_openlibs) (lua_State *L); From 041092a6f4d033aa874a7520444f2adf952bd5a9 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 15 May 2023 19:50:46 +0200 Subject: [PATCH 33/49] Print progress in verbose arp-scanning mode Signed-off-by: DL6ER --- src/tools/arp-scan.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/arp-scan.c b/src/tools/arp-scan.c index e549cfa2c..3d7ccb8dc 100644 --- a/src/tools/arp-scan.c +++ b/src/tools/arp-scan.c @@ -364,9 +364,9 @@ static void *arp_scan_iface(void *args) for(unsigned int scan_id = 0; scan_id < NUM_SCANS; scan_id++) { -#ifdef DEBUG - printf("Scanning interface %s (%s/%i) for the %i. time\n", iface, ipstr, cidr, scan_id + 1); -#endif + if(arp_verbose) + printf("Still scanning interface %s (%s/%i) %i%%...\n", iface, ipstr, cidr, 100*scan_id/NUM_SCANS); + // Send ARP requests to all IPs in subnet if(send_arps(arp_socket, ifindex, iface, mac, &src_addr.sin_addr, dst_addr, cidr) != 0) break; From 1e0f20b2bc7306561d06dcca0ea89746787deafc Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 15 May 2023 19:54:52 +0200 Subject: [PATCH 34/49] Print different warnings if we received multiple replies from (apparently) the same device or if we received replies for the same address from different MAC addresses Signed-off-by: DL6ER --- src/tools/arp-scan.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/tools/arp-scan.c b/src/tools/arp-scan.c index 3d7ccb8dc..ffbf73788 100644 --- a/src/tools/arp-scan.c +++ b/src/tools/arp-scan.c @@ -406,7 +406,7 @@ static void *arp_scan_iface(void *args) for(unsigned int i = 0; i < arp_result_len; i++) { unsigned int j = 0, replied_devices = 0; - bool multiple_replies = false; + unsigned int multiple_replies = 0; // Print MAC addresses for(j = 0; j < MAX_MACS; j++) @@ -416,7 +416,7 @@ static void *arp_scan_iface(void *args) for(unsigned int k = 0; k < NUM_SCANS; k++) { replied |= result[i].device[j].replied[k] > 0; - multiple_replies |= result[i].device[j].replied[k] > 1; + multiple_replies += result[i].device[j].replied[k] > 1; } if(!replied) continue; @@ -449,8 +449,12 @@ static void *arp_scan_iface(void *args) } // Print warning if we received multiple replies - if(replied_devices > 1 || multiple_replies) - printf("WARNING: Received multiple replies for %s\n", ipstr); + if(replied_devices > 1) + printf("WARNING: Received replies for %s from %i devices\n", + ipstr, replied_devices); + if(multiple_replies > 0) + printf("WARNING: Received multiple replies for %s in %i scan%s\n", + ipstr, multiple_replies, multiple_replies > 1 ? "s" : ""); } putc('\n', stdout); From f1b4a338f04aa48536e91587b2e7e86b9885927a Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 15 May 2023 20:58:37 +0200 Subject: [PATCH 35/49] Consolidate output in main process Signed-off-by: DL6ER --- src/args.c | 6 +- src/tools/arp-scan.c | 248 +++++++++++++++++++++++++++++-------------- src/tools/arp-scan.h | 2 +- 3 files changed, 172 insertions(+), 84 deletions(-) diff --git a/src/args.c b/src/args.c index f86a19e30..14b4031d5 100644 --- a/src/args.c +++ b/src/args.c @@ -178,9 +178,8 @@ void parse_args(int argc, char* argv[]) { // Enable stdout printing cli_mode = true; - const bool verbose = argc > 2 && strcmp(argv[2], "-v") == 0; const bool arp_all = argc > 2 && strcmp(argv[2], "-a") == 0; - exit(run_arp_scan(verbose, arp_all)); + exit(run_arp_scan(arp_all)); } // start from 1, as argv[0] is the executable name @@ -514,9 +513,8 @@ void parse_args(int argc, char* argv[]) printf("%sOther:%s\n", yellow, normal); printf("\t%sdhcp-discover%s Discover DHCP servers in the local\n", green, normal); printf("\t network\n"); - printf("\t%sarp-scan %s[{-v/-a}]%s Use ARP to scan local network for\n", green, cyan, normal); + printf("\t%sarp-scan %s[-a]%s Use ARP to scan local network for\n", green, cyan, normal); printf("\t possible IP conflicts\n"); - printf("\t Append %s-v%s for verbose output mode\n", cyan, normal); printf("\t Append %s-a%s to force scan on all\n", cyan, normal); printf("\t interfaces\n"); printf("\t%s-h%s, %shelp%s Display this help and exit\n\n", green, normal, green, normal); diff --git a/src/tools/arp-scan.c b/src/tools/arp-scan.c index ffbf73788..69e702412 100644 --- a/src/tools/arp-scan.c +++ b/src/tools/arp-scan.c @@ -14,6 +14,8 @@ #include "log.h" // get_hardware_address() #include "dhcp-discover.h" +// sleepms() +#include "timers.h" #include #include @@ -35,9 +37,7 @@ // How long do we wait for ARP replies in each scan [seconds]? #define ARP_TIMEOUT 1 -// Global lock used by all threads -static pthread_mutex_t lock; -static bool arp_verbose = false; +// Global constant static bool arp_all = false; // Protocol definitions @@ -73,6 +73,30 @@ struct arp_result { } device[MAX_MACS]; }; +enum status { + STATUS_INITIALIZING = 0, + STATUS_SKIPPED_CIDR_MISMATCH, + STATUS_SCANNING, + STATUS_ERROR, + STATUS_COMPLETE +}; + +struct thread_data { + int dst_cidr; + struct sockaddr_in src_addr; + struct sockaddr_in dst_addr; + struct sockaddr_in mask; + struct ifaddrs *ifa; + const char *iface; + struct arp_result *result; + size_t result_size; + enum status status; + char ipstr[INET_ADDRSTRLEN]; + unsigned char mac[16]; + unsigned int num_scans; + char *error; +}; + // Sends multiple ARP who-has request on interface ifindex, using source mac src_mac and source ip src_ip. // Iterates over all IP addresses in the range of dst_ip/cidr. static int send_arps(const int fd, const int ifindex, const char *iface, const unsigned char *src_mac, @@ -195,7 +219,7 @@ static void add_result(const char *iface, struct in_addr *rcv_ip, struct in_addr uint32_t i = ntohl(rcv_ip->s_addr) - ntohl(dst_ip->s_addr); if(i >= result_len) { - printf("Received IP address %s out of range\n", inet_ntoa(*rcv_ip)); + printf("Received IP address %s out of range for interface %s (%u >= %zu)\n", inet_ntoa(*rcv_ip), iface, i, result_len); return; } @@ -307,103 +331,132 @@ static const char *get_hostname(const struct in_addr *addr) static void *arp_scan_iface(void *args) { + // Get thread_data pointer + struct thread_data *thread_data = (struct thread_data*)args; + // Get interface details - struct ifaddrs *ifa = (struct ifaddrs*)args; + struct ifaddrs *ifa = thread_data->ifa; // Get interface name - const char *iface = ifa->ifa_name; + const char *iface = thread_data->iface; // Set interface name as thread name prctl(PR_SET_NAME, iface, 0, 0, 0); // Get interface IPv4 address - struct sockaddr_in src_addr = { 0 }; - memcpy(&src_addr, ((struct ifaddrs*)args)->ifa_addr, sizeof(src_addr)); - char ipstr[INET_ADDRSTRLEN] = { 0 }; - inet_ntop(AF_INET, &src_addr.sin_addr, ipstr, INET_ADDRSTRLEN); + memcpy(&thread_data->src_addr, ifa->ifa_addr, sizeof(thread_data->src_addr)); + inet_ntop(AF_INET, &thread_data->src_addr.sin_addr, thread_data->ipstr, INET_ADDRSTRLEN); // Get interface netmask - struct sockaddr_in mask = { 0 }; - memcpy(&mask, ((struct ifaddrs*)args)->ifa_netmask, sizeof(mask)); - // char netmask[INET_ADDRSTRLEN] = { 0 }; - // inet_ntop(AF_INET, &mask.sin_addr, netmask, INET_ADDRSTRLEN); + memcpy(&thread_data->mask, ifa->ifa_netmask, sizeof(thread_data->mask)); // Convert subnet to CIDR - const int cidr = netmask_to_cidr(&mask.sin_addr); + thread_data->dst_cidr = netmask_to_cidr(&thread_data->mask.sin_addr); // Get interface index const int ifindex = if_nametoindex(iface); // Scan only interfaces with CIDR >= 24 - if(cidr < 24 && !arp_all) + if(thread_data->dst_cidr < 24 && !arp_all) { - printf("Skipped interface %s (%s/%i)\n", iface, ipstr, cidr); + thread_data->status = STATUS_SKIPPED_CIDR_MISMATCH; + //printf("Skipped interface %s (%s/%i)\n", iface, thread_data->ipstr, thread_data->dst_cidr); pthread_exit(NULL); } - if(arp_verbose) - printf("Scanning interface %s (%s/%i)...\n", iface, ipstr, cidr); + //if(arp_verbose) + // printf("Scanning interface %s (%s/%i)...\n", iface, thread_data->ipstr, thread_data->dst_cidr); + thread_data->status = STATUS_SCANNING; // Create socket for ARP communications const int arp_socket = create_arp_socket(ifindex, iface); // Cannot create socket, likely a permission error if(arp_socket < 0) + { + thread_data->status = STATUS_ERROR; pthread_exit(NULL); + } // Get hardware address of client machine - unsigned char mac[16] = { 0 }; - get_hardware_address(arp_socket, iface, mac); + get_hardware_address(arp_socket, iface, thread_data->mac); // Define destination IP address by masking source IP with netmask - struct in_addr dst_addr = { 0 }; - dst_addr.s_addr = src_addr.sin_addr.s_addr & mask.sin_addr.s_addr; + thread_data->dst_addr.sin_addr.s_addr = thread_data->src_addr.sin_addr.s_addr & thread_data->mask.sin_addr.s_addr; // Allocate memory for ARP response buffer - const size_t arp_result_len = 1 << (32 - cidr); - struct arp_result *result = calloc(arp_result_len, sizeof(struct arp_result)); + const size_t arp_result_len = 1 << (32 - thread_data->dst_cidr); + thread_data->result_size = arp_result_len; + struct arp_result *result = calloc(thread_data->result_size, sizeof(struct arp_result)); + thread_data->result = result; - for(unsigned int scan_id = 0; scan_id < NUM_SCANS; scan_id++) + for(thread_data->num_scans = 0; thread_data->num_scans < NUM_SCANS; thread_data->num_scans++) { - if(arp_verbose) - printf("Still scanning interface %s (%s/%i) %i%%...\n", iface, ipstr, cidr, 100*scan_id/NUM_SCANS); + //if(arp_verbose) + // printf("Still scanning interface %s (%s/%i) %i%%...\n", iface, thread_data->ipstr, thread_data->dst_cidr, 100*scan_id/NUM_SCANS); // Send ARP requests to all IPs in subnet - if(send_arps(arp_socket, ifindex, iface, mac, &src_addr.sin_addr, dst_addr, cidr) != 0) + if(send_arps(arp_socket, ifindex, iface, thread_data->mac, &thread_data->src_addr.sin_addr, thread_data->dst_addr.sin_addr, thread_data->dst_cidr) != 0) + { + thread_data->status = STATUS_ERROR; break; + } // Read ARP responses - if(read_arp(arp_socket, iface, &dst_addr, result, arp_result_len, scan_id) != 0) + if(read_arp(arp_socket, iface, &thread_data->dst_addr.sin_addr, thread_data->result, thread_data->result_size, thread_data->num_scans) != 0) + { + thread_data->status = STATUS_ERROR; break; + } + } + + // Close socket + if(close(arp_socket) != 0) + thread_data->status = STATUS_ERROR; + + if(thread_data->status != STATUS_ERROR) + thread_data->status = STATUS_COMPLETE; + + pthread_exit(NULL); +} + +static void print_results(struct thread_data *thread_data) +{ + + if(thread_data->status == STATUS_SKIPPED_CIDR_MISMATCH) + { + printf("Skipped interface %s (%s/%i) because of too large network (use -a to force scanning this interface)\n\n", + thread_data->iface, thread_data->ipstr, thread_data->dst_cidr); + return; } // Check if there are any results unsigned int replies = 0; - for(unsigned int i = 0; i < arp_result_len; i++) + for(unsigned int i = 0; i < thread_data->result_size; i++) for(unsigned int j = 0; j < MAX_MACS; j++) for(unsigned int k = 0; k < NUM_SCANS; k++) - replies += result[i].device[j].replied[k]; - - if(pthread_mutex_lock(&lock) != 0) - return NULL; + replies += thread_data->result[i].device[j].replied[k]; // Exit early if there are no results if(replies == 0) { - printf("No devices found on interface %s (%s/%i)\n", iface, ipstr, cidr); - goto arp_scan_iface_end; + printf("No devices found on interface %s (%s/%i)\n\n", + thread_data->iface, thread_data->ipstr, thread_data->dst_cidr); + return; } // If there is at least one result, print header - printf("ARP scan on interface %s (%s/%i) finished\n", iface, ipstr, cidr); - printf("%-16s %-10s %-24s %-17s Reply matrix\n", "IP address", "Interface", "Hostname", "MAC address"); + printf("ARP scan on interface %s (%s/%i) finished\n", + thread_data->iface, thread_data->ipstr, thread_data->dst_cidr); + printf("%-16s %-10s %-24s %-17s Reply matrix\n", + "IP address", "Interface", "Hostname", "MAC address"); // Add our own IP address to the results so IP conflicts can be detected // (our own IP address is not included in the ARP scan) for(unsigned int i = 0; i < NUM_SCANS; i++) - add_result(iface, &src_addr.sin_addr, &dst_addr, mac, result, arp_result_len, i); + add_result(thread_data->iface, &thread_data->src_addr.sin_addr, &thread_data->dst_addr.sin_addr, thread_data->mac, thread_data->result, thread_data->result_size, i); // Print results - for(unsigned int i = 0; i < arp_result_len; i++) + for(unsigned int i = 0; i < thread_data->result_size; i++) { unsigned int j = 0, replied_devices = 0; unsigned int multiple_replies = 0; @@ -415,8 +468,8 @@ static void *arp_scan_iface(void *args) bool replied = false; for(unsigned int k = 0; k < NUM_SCANS; k++) { - replied |= result[i].device[j].replied[k] > 0; - multiple_replies += result[i].device[j].replied[k] > 1; + replied |= thread_data->result[i].device[j].replied[k] > 0; + multiple_replies += thread_data->result[i].device[j].replied[k] > 1; } if(!replied) continue; @@ -426,24 +479,25 @@ static void *arp_scan_iface(void *args) // Convert IP address to string struct in_addr ip = { 0 }; - ip.s_addr = htonl(ntohl(dst_addr.s_addr) + i); - inet_ntop(AF_INET, &ip, ipstr, INET_ADDRSTRLEN); + ip.s_addr = htonl(ntohl(thread_data->dst_addr.sin_addr.s_addr) + i); + inet_ntop(AF_INET, &ip, thread_data->ipstr, INET_ADDRSTRLEN); // Check if result[i].mac[j] is all-zero - if(memcmp(result[i].device[j].mac, "\x00\x00\x00\x00\x00\x00", 6) == 0) + if(memcmp(thread_data->result[i].device[j].mac, "\x00\x00\x00\x00\x00\x00", 6) == 0) break; // Print MAC address printf("%-16s %-10s %-24s %02x:%02x:%02x:%02x:%02x:%02x ", - ipstr, iface, get_hostname(&ip), - result[i].device[j].mac[0], - result[i].device[j].mac[1], - result[i].device[j].mac[2], - result[i].device[j].mac[3], - result[i].device[j].mac[4], - result[i].device[j].mac[5]); + thread_data->ipstr, thread_data->iface, + get_hostname(&ip), + thread_data->result[i].device[j].mac[0], + thread_data->result[i].device[j].mac[1], + thread_data->result[i].device[j].mac[2], + thread_data->result[i].device[j].mac[3], + thread_data->result[i].device[j].mac[4], + thread_data->result[i].device[j].mac[5]); for(unsigned int k = 0; k < NUM_SCANS; k++) - printf(" %s", result[i].device[j].replied[k] > 0 ? "X" : "-"); + printf(" %s", thread_data->result[i].device[j].replied[k] > 0 ? "X" : "-"); putc('\n', stdout); } @@ -451,25 +505,16 @@ static void *arp_scan_iface(void *args) // Print warning if we received multiple replies if(replied_devices > 1) printf("WARNING: Received replies for %s from %i devices\n", - ipstr, replied_devices); + thread_data->ipstr, replied_devices); if(multiple_replies > 0) printf("WARNING: Received multiple replies for %s in %i scan%s\n", - ipstr, multiple_replies, multiple_replies > 1 ? "s" : ""); + thread_data->ipstr, multiple_replies, multiple_replies > 1 ? "s" : ""); } putc('\n', stdout); - -arp_scan_iface_end: - if(pthread_mutex_unlock(&lock) != 0) - return NULL; - - // Close socket - close(arp_socket); - pthread_exit(NULL); } -int run_arp_scan(const bool verbose, const bool scan_all) +int run_arp_scan(const bool scan_all) { - arp_verbose = verbose; arp_all = scan_all; puts("Discovering IPv4 hosts on the network using the Address Resolution Protocol (ARP)...\n"); @@ -480,28 +525,25 @@ int run_arp_scan(const bool verbose, const bool scan_all) // Initialize thread attributes object with default attribute values pthread_attr_init(&attr); - // Create processing/logging lock - pthread_mutexattr_t lock_attr = {}; - // Initialize the lock attributes - pthread_mutexattr_init(&lock_attr); - // Initialize the lock - pthread_mutex_init(&lock, &lock_attr); - // Destroy the lock attributes since we're done with it - pthread_mutexattr_destroy(&lock_attr); - struct ifaddrs *addrs, *tmp; getifaddrs(&addrs); tmp = addrs; // Loop until there are no more interfaces available // or we reached the maximum number of threads - int tid = 0; + unsigned int tid = 0; + + struct thread_data thread_data[MAXTHREADS] = {0}; + while(tmp != NULL && tid < MAXTHREADS) { // Create a thread for interfaces of type AF_INET if(tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_INET) { - if(pthread_create(&scanthread[tid], &attr, arp_scan_iface, tmp ) != 0) + thread_data[tid].ifa = tmp; + thread_data[tid].iface = tmp->ifa_name; + // Create thread + if(pthread_create(&scanthread[tid], &attr, arp_scan_iface, &thread_data[tid] ) != 0) { printf("Unable to launch thread for interface %s, skipping...\n", tmp->ifa_name); @@ -516,12 +558,60 @@ int run_arp_scan(const bool verbose, const bool scan_all) tmp = tmp->ifa_next; } + // Wait for all threads to finish scanning + bool all_done = false; + while(!all_done) + { + all_done = true; + unsigned int num_scans = 0, total_scans = 0; + for(unsigned int i = 0; i < tid; i++) + { + if(thread_data[i].status == STATUS_SCANNING) + { + // At least one thread is still scanning + all_done = false; + num_scans += thread_data[i].num_scans; + total_scans += NUM_SCANS; + } + if(thread_data[i].status == STATUS_COMPLETE) + { + // Also add up scans for completed threads + num_scans += thread_data[i].num_scans; + total_scans += NUM_SCANS; + } + } + if(!all_done) + { + // Print progress + printf("%i%%... ", 100*num_scans/total_scans); + // Flush stdout + fflush(stdout); + // Sleep for 1 second + sleepms(1000); + } + } + puts("100%%\n\n"); + // Wait for all threads to join back with us - for(tid--; tid > -1; tid--) - pthread_join(scanthread[tid], NULL); + for(unsigned int i = 0; i < tid; i++) + pthread_join(scanthread[i], NULL); + + // Destroy the thread attributes object, since we are done with it + pthread_attr_destroy(&attr); // Free linked-list of interfaces on this client freeifaddrs(addrs); + // Loop over thread results and print them + for(unsigned int i = 0; i < tid; i++) + { + // Print results + print_results(&thread_data[i]); + + // Free allocated memory + if(thread_data[i].result != NULL) + free(thread_data[i].result); + } + return EXIT_SUCCESS; } diff --git a/src/tools/arp-scan.h b/src/tools/arp-scan.h index 90b5fec70..0a5b56711 100644 --- a/src/tools/arp-scan.h +++ b/src/tools/arp-scan.h @@ -11,6 +11,6 @@ #ifndef ARP_SCAN_H #define ARP_SCAN_H -int run_arp_scan(const bool verbose, const bool scan_all); +int run_arp_scan(const bool scan_all); #endif // ARP_SCAN_H From 5dcdbd7264b98e8d69269d60af1789779eb6477e Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 16 May 2023 04:40:11 +0200 Subject: [PATCH 36/49] Scale progress percentage according to number of addresses to be scanned by the individual threads Signed-off-by: DL6ER --- src/tools/arp-scan.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/tools/arp-scan.c b/src/tools/arp-scan.c index 69e702412..b84ac4dd1 100644 --- a/src/tools/arp-scan.c +++ b/src/tools/arp-scan.c @@ -563,34 +563,35 @@ int run_arp_scan(const bool scan_all) while(!all_done) { all_done = true; - unsigned int num_scans = 0, total_scans = 0; + uint64_t num_scans = 0, total_scans = 0; for(unsigned int i = 0; i < tid; i++) { - if(thread_data[i].status == STATUS_SCANNING) + const uint32_t num_addresses = 1 << (32 - thread_data[i].dst_cidr); + if(thread_data[i].status == STATUS_INITIALIZING || + thread_data[i].status == STATUS_SCANNING) { // At least one thread is still scanning all_done = false; - num_scans += thread_data[i].num_scans; - total_scans += NUM_SCANS; } - if(thread_data[i].status == STATUS_COMPLETE) + if(thread_data[i].status == STATUS_SCANNING || + thread_data[i].status == STATUS_COMPLETE) { // Also add up scans for completed threads - num_scans += thread_data[i].num_scans; - total_scans += NUM_SCANS; + num_scans += thread_data[i].num_scans * num_addresses; + total_scans += NUM_SCANS * num_addresses; } } if(!all_done) { // Print progress - printf("%i%%... ", 100*num_scans/total_scans); + printf("%i%%... ", (int)(100*num_scans/total_scans)); // Flush stdout fflush(stdout); // Sleep for 1 second sleepms(1000); } } - puts("100%%\n\n"); + puts("100%\n\n"); // Wait for all threads to join back with us for(unsigned int i = 0; i < tid; i++) From 1bc05b9affc82c3ff9f6a7a054bc48576de36270 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 16 May 2023 04:45:39 +0200 Subject: [PATCH 37/49] Only print progress if it has changed. Otherwise, print "." as hearthbeat Signed-off-by: DL6ER --- src/tools/arp-scan.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/src/tools/arp-scan.c b/src/tools/arp-scan.c index b84ac4dd1..8fb5351e3 100644 --- a/src/tools/arp-scan.c +++ b/src/tools/arp-scan.c @@ -94,13 +94,14 @@ struct thread_data { char ipstr[INET_ADDRSTRLEN]; unsigned char mac[16]; unsigned int num_scans; + uint32_t scanned_addresses; char *error; }; // Sends multiple ARP who-has request on interface ifindex, using source mac src_mac and source ip src_ip. // Iterates over all IP addresses in the range of dst_ip/cidr. static int send_arps(const int fd, const int ifindex, const char *iface, const unsigned char *src_mac, - struct in_addr *src_ip, struct in_addr dst_ip, const int dst_cidr) + struct in_addr *src_ip, struct in_addr dst_ip, const int dst_cidr, uint32_t *scanned_addresses) { int err = -1; unsigned char buffer[BUF_SIZE]; @@ -168,6 +169,8 @@ static int send_arps(const int fd, const int ifindex, const char *iface, const u // Increment IP address dst_ip.s_addr = htonl(ntohl(dst_ip.s_addr) + 1); + + (*scanned_addresses)++; } err = 0; @@ -395,14 +398,16 @@ static void *arp_scan_iface(void *args) // printf("Still scanning interface %s (%s/%i) %i%%...\n", iface, thread_data->ipstr, thread_data->dst_cidr, 100*scan_id/NUM_SCANS); // Send ARP requests to all IPs in subnet - if(send_arps(arp_socket, ifindex, iface, thread_data->mac, &thread_data->src_addr.sin_addr, thread_data->dst_addr.sin_addr, thread_data->dst_cidr) != 0) + if(send_arps(arp_socket, ifindex, iface, thread_data->mac, &thread_data->src_addr.sin_addr, + thread_data->dst_addr.sin_addr, thread_data->dst_cidr, &thread_data->scanned_addresses) != 0) { thread_data->status = STATUS_ERROR; break; } // Read ARP responses - if(read_arp(arp_socket, iface, &thread_data->dst_addr.sin_addr, thread_data->result, thread_data->result_size, thread_data->num_scans) != 0) + if(read_arp(arp_socket, iface, &thread_data->dst_addr.sin_addr, thread_data->result, thread_data->result_size, + thread_data->num_scans) != 0) { thread_data->status = STATUS_ERROR; break; @@ -560,13 +565,13 @@ int run_arp_scan(const bool scan_all) // Wait for all threads to finish scanning bool all_done = false; + unsigned int progress = 0; while(!all_done) { all_done = true; uint64_t num_scans = 0, total_scans = 0; for(unsigned int i = 0; i < tid; i++) { - const uint32_t num_addresses = 1 << (32 - thread_data[i].dst_cidr); if(thread_data[i].status == STATUS_INITIALIZING || thread_data[i].status == STATUS_SCANNING) { @@ -577,16 +582,29 @@ int run_arp_scan(const bool scan_all) thread_data[i].status == STATUS_COMPLETE) { // Also add up scans for completed threads - num_scans += thread_data[i].num_scans * num_addresses; - total_scans += NUM_SCANS * num_addresses; + num_scans += thread_data[i].scanned_addresses; + total_scans += NUM_SCANS * thread_data[i].result_size; } } if(!all_done) { - // Print progress - printf("%i%%... ", (int)(100*num_scans/total_scans)); + // Calculate progress (total number of scans / total number of addresses) + // We add 1 to total_scans to avoid division by zero + const unsigned int new_progress = 100 * num_scans / (total_scans + 1); + if(new_progress > progress) + { + // Print progress + printf(" %i%%", new_progress); + + // Update progress + progress = new_progress; + } + + putc('.', stdout); + // Flush stdout fflush(stdout); + // Sleep for 1 second sleepms(1000); } From 4387ff21ee8bf15f65adaf29f98b5b187354e370 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 16 May 2023 04:49:19 +0200 Subject: [PATCH 38/49] Always skip the loopback interface, also in "-a" mode Signed-off-by: DL6ER --- src/tools/arp-scan.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/tools/arp-scan.c b/src/tools/arp-scan.c index 8fb5351e3..1a038b3a8 100644 --- a/src/tools/arp-scan.c +++ b/src/tools/arp-scan.c @@ -346,10 +346,6 @@ static void *arp_scan_iface(void *args) // Set interface name as thread name prctl(PR_SET_NAME, iface, 0, 0, 0); - // Get interface IPv4 address - memcpy(&thread_data->src_addr, ifa->ifa_addr, sizeof(thread_data->src_addr)); - inet_ntop(AF_INET, &thread_data->src_addr.sin_addr, thread_data->ipstr, INET_ADDRSTRLEN); - // Get interface netmask memcpy(&thread_data->mask, ifa->ifa_netmask, sizeof(thread_data->mask)); @@ -547,16 +543,24 @@ int run_arp_scan(const bool scan_all) { thread_data[tid].ifa = tmp; thread_data[tid].iface = tmp->ifa_name; - // Create thread - if(pthread_create(&scanthread[tid], &attr, arp_scan_iface, &thread_data[tid] ) != 0) + + // Get interface IPv4 address + memcpy(&thread_data[tid].src_addr, tmp->ifa_addr, sizeof(thread_data[tid].src_addr)); + inet_ntop(AF_INET, &thread_data[tid].src_addr.sin_addr, thread_data[tid].ipstr, INET_ADDRSTRLEN); + + // Always skip the loopback interface + if(thread_data[tid].src_addr.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) { - printf("Unable to launch thread for interface %s, skipping...\n", - tmp->ifa_name); - continue; + // Create thread + if(pthread_create(&scanthread[tid], &attr, arp_scan_iface, &thread_data[tid] ) != 0) + { + printf("Unable to launch thread for interface %s, skipping...\n", + tmp->ifa_name); + } + + // Increase thread ID + tid++; } - - // Increase thread ID - tid++; } // Advance to the next interface From d45a7c46fc23c32bed0906207be7efc7dced8a0b Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 16 May 2023 04:51:01 +0200 Subject: [PATCH 39/49] Interface names can be up to 16 bytes long. Docker bridge interfaces actually use this space so we need to reserve enough space here Signed-off-by: DL6ER --- src/tools/arp-scan.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/arp-scan.c b/src/tools/arp-scan.c index 1a038b3a8..1c446840f 100644 --- a/src/tools/arp-scan.c +++ b/src/tools/arp-scan.c @@ -448,7 +448,7 @@ static void print_results(struct thread_data *thread_data) // If there is at least one result, print header printf("ARP scan on interface %s (%s/%i) finished\n", thread_data->iface, thread_data->ipstr, thread_data->dst_cidr); - printf("%-16s %-10s %-24s %-17s Reply matrix\n", + printf("%-16s %-16s %-24s %-17s Reply matrix\n", "IP address", "Interface", "Hostname", "MAC address"); // Add our own IP address to the results so IP conflicts can be detected @@ -487,7 +487,7 @@ static void print_results(struct thread_data *thread_data) break; // Print MAC address - printf("%-16s %-10s %-24s %02x:%02x:%02x:%02x:%02x:%02x ", + printf("%-16s %-16s %-24s %02x:%02x:%02x:%02x:%02x:%02x ", thread_data->ipstr, thread_data->iface, get_hostname(&ip), thread_data->result[i].device[j].mac[0], From 2d6a3619a07fb5a41ba95d232912ded1d0149c6b Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 16 May 2023 11:49:11 +0200 Subject: [PATCH 40/49] Clearly log when scanning interfaces failed Signed-off-by: DL6ER --- src/tools/arp-scan.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/tools/arp-scan.c b/src/tools/arp-scan.c index 1c446840f..60b60d700 100644 --- a/src/tools/arp-scan.c +++ b/src/tools/arp-scan.c @@ -430,6 +430,13 @@ static void print_results(struct thread_data *thread_data) return; } + if(thread_data->status == STATUS_ERROR) + { + printf("Error scanning interface %s (%s/%i)\n\n", + thread_data->iface, thread_data->ipstr, thread_data->dst_cidr); + return; + } + // Check if there are any results unsigned int replies = 0; for(unsigned int i = 0; i < thread_data->result_size; i++) @@ -440,7 +447,7 @@ static void print_results(struct thread_data *thread_data) // Exit early if there are no results if(replies == 0) { - printf("No devices found on interface %s (%s/%i)\n\n", + printf("No devices replied on interface %s (%s/%i)\n\n", thread_data->iface, thread_data->ipstr, thread_data->dst_cidr); return; } From 8a299112b83a9527dac385fc1106aaf41814ca4c Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 16 May 2023 20:54:51 +0200 Subject: [PATCH 41/49] Log more verbose human-readable error string if available Signed-off-by: DL6ER --- src/tools/arp-scan.c | 53 +++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/src/tools/arp-scan.c b/src/tools/arp-scan.c index 60b60d700..c3eef0e81 100644 --- a/src/tools/arp-scan.c +++ b/src/tools/arp-scan.c @@ -95,12 +95,12 @@ struct thread_data { unsigned char mac[16]; unsigned int num_scans; uint32_t scanned_addresses; - char *error; + const char *error; }; // Sends multiple ARP who-has request on interface ifindex, using source mac src_mac and source ip src_ip. // Iterates over all IP addresses in the range of dst_ip/cidr. -static int send_arps(const int fd, const int ifindex, const char *iface, const unsigned char *src_mac, +static int send_arps(const int fd, const int ifindex, const char *iface, const unsigned char *src_mac, const char **error, struct in_addr *src_ip, struct in_addr dst_ip, const int dst_cidr, uint32_t *scanned_addresses) { int err = -1; @@ -161,9 +161,8 @@ static int send_arps(const int fd, const int ifindex, const char *iface, const u ret = sendto(fd, buffer, 42, 0, (struct sockaddr *) &socket_address, sizeof(socket_address)); if (ret == -1) { - if(errno != EPROTONOSUPPORT) - printf("Unable to send ARP request for %s@%s: %s\n", - inet_ntoa(dst_ip), iface, strerror(errno)); + err = errno; + *error = strerror(err); goto out; } @@ -178,13 +177,14 @@ static int send_arps(const int fd, const int ifindex, const char *iface, const u return err; } -static int create_arp_socket(const int ifindex, const char *iface) +static int create_arp_socket(const int ifindex, const char *iface, const char **error) { // Create socket for ARP communications const int arp_socket = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP)); if(arp_socket < 0) { - printf("Unable to create socket for ARP communications on interface %s: %s\n", iface, strerror(errno)); + *error = strerror(errno); + printf("Unable to create socket for ARP communications on interface %s: %s\n", iface, *error); return -1; } @@ -195,7 +195,8 @@ static int create_arp_socket(const int ifindex, const char *iface) sll.sll_ifindex = ifindex; if (bind(arp_socket, (struct sockaddr*) &sll, sizeof(struct sockaddr_ll)) < 0) { - printf("Unable to bind socket for ARP communications on interface %s: %s\n", iface, strerror(errno)); + *error = strerror(errno); + printf("Unable to bind socket for ARP communications on interface %s: %s\n", iface, *error); close(arp_socket); return -1; } @@ -206,7 +207,8 @@ static int create_arp_socket(const int ifindex, const char *iface) tv.tv_usec = 0; if (setsockopt(arp_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) { - printf("Unable to set timeout for ARP communications on interface %s: %s\n", iface, strerror(errno)); + *error = strerror(errno); + printf("Unable to set timeout for ARP communications on interface %s: %s\n", iface, *error); close(arp_socket); return -1; } @@ -249,7 +251,7 @@ static void add_result(const char *iface, struct in_addr *rcv_ip, struct in_addr } // Read all ARP responses -static ssize_t read_arp(const int fd, const char *iface, struct in_addr *dst_ip, +static ssize_t read_arp(const int fd, const char *iface, struct in_addr *dst_ip, const char **error, struct arp_result *result, const size_t result_len, const unsigned int scan_id) { ssize_t ret = 0; @@ -269,7 +271,8 @@ static ssize_t read_arp(const int fd, const char *iface, struct in_addr *dst_ip, } // Error - printf("recvfrom(): %s", strerror(errno)); + *error = strerror(errno); + printf("recvfrom(): %s", *error); break; } struct ethhdr *rcv_resp = (struct ethhdr *) buffer; @@ -359,15 +362,18 @@ static void *arp_scan_iface(void *args) if(thread_data->dst_cidr < 24 && !arp_all) { thread_data->status = STATUS_SKIPPED_CIDR_MISMATCH; - //printf("Skipped interface %s (%s/%i)\n", iface, thread_data->ipstr, thread_data->dst_cidr); +#ifdef DEBUG + printf("Skipped interface %s (%s/%i)\n", iface, thread_data->ipstr, thread_data->dst_cidr); +#endif pthread_exit(NULL); } - //if(arp_verbose) - // printf("Scanning interface %s (%s/%i)...\n", iface, thread_data->ipstr, thread_data->dst_cidr); +#ifdef DEBUG + printf("Scanning interface %s (%s/%i)...\n", iface, thread_data->ipstr, thread_data->dst_cidr); +#endif thread_data->status = STATUS_SCANNING; // Create socket for ARP communications - const int arp_socket = create_arp_socket(ifindex, iface); + const int arp_socket = create_arp_socket(ifindex, iface, &thread_data->error); // Cannot create socket, likely a permission error if(arp_socket < 0) @@ -390,11 +396,11 @@ static void *arp_scan_iface(void *args) for(thread_data->num_scans = 0; thread_data->num_scans < NUM_SCANS; thread_data->num_scans++) { - //if(arp_verbose) - // printf("Still scanning interface %s (%s/%i) %i%%...\n", iface, thread_data->ipstr, thread_data->dst_cidr, 100*scan_id/NUM_SCANS); - +#ifdef DEBUG + printf("Still scanning interface %s (%s/%i) %i%%...\n", iface, thread_data->ipstr, thread_data->dst_cidr, 100*scan_id/NUM_SCANS); +#endif // Send ARP requests to all IPs in subnet - if(send_arps(arp_socket, ifindex, iface, thread_data->mac, &thread_data->src_addr.sin_addr, + if(send_arps(arp_socket, ifindex, iface, thread_data->mac, &thread_data->error, &thread_data->src_addr.sin_addr, thread_data->dst_addr.sin_addr, thread_data->dst_cidr, &thread_data->scanned_addresses) != 0) { thread_data->status = STATUS_ERROR; @@ -402,8 +408,8 @@ static void *arp_scan_iface(void *args) } // Read ARP responses - if(read_arp(arp_socket, iface, &thread_data->dst_addr.sin_addr, thread_data->result, thread_data->result_size, - thread_data->num_scans) != 0) + if(read_arp(arp_socket, iface, &thread_data->dst_addr.sin_addr, &thread_data->error, + thread_data->result, thread_data->result_size, thread_data->num_scans) != 0) { thread_data->status = STATUS_ERROR; break; @@ -432,8 +438,9 @@ static void print_results(struct thread_data *thread_data) if(thread_data->status == STATUS_ERROR) { - printf("Error scanning interface %s (%s/%i)\n\n", - thread_data->iface, thread_data->ipstr, thread_data->dst_cidr); + printf("Error scanning interface %s (%s/%i)%s%s\n\n", + thread_data->iface, thread_data->ipstr, thread_data->dst_cidr, + thread_data->error ? ": " : "", thread_data->error ? thread_data->error : ""); return; } From e3550c1a472d1bcdf624277edf66e1dcce86b94f Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 16 May 2023 21:02:29 +0200 Subject: [PATCH 42/49] Add capabilities check for CAP_NET_RAW (root always has it) Signed-off-by: DL6ER --- src/capabilities.c | 41 +++++++++++++++++++++++++++++++++++++++++ src/capabilities.h | 1 + src/tools/arp-scan.c | 16 ++++++++++++++++ 3 files changed, 58 insertions(+) diff --git a/src/capabilities.c b/src/capabilities.c index 603cb4f76..49e5d5d96 100644 --- a/src/capabilities.c +++ b/src/capabilities.c @@ -21,6 +21,47 @@ static const unsigned int capabilityIDs[] = { CAP_CHOWN , CAP_DAC_OVERRIDE , static const char* capabilityNames[] = {"CAP_CHOWN", "CAP_DAC_OVERRIDE", "CAP_DAC_READ_SEARCH", "CAP_FOWNER", "CAP_FSETID", "CAP_KILL", "CAP_SETGID", "CAP_SETUID", "CAP_SETPCAP", "CAP_LINUX_IMMUTABLE", "CAP_NET_BIND_SERVICE", "CAP_NET_BROADCAST", "CAP_NET_ADMIN", "CAP_NET_RAW", "CAP_IPC_LOCK", "CAP_IPC_OWNER", "CAP_SYS_MODULE", "CAP_SYS_RAWIO", "CAP_SYS_CHROOT", "CAP_SYS_PTRACE", "CAP_SYS_PACCT", "CAP_SYS_ADMIN", "CAP_SYS_BOOT", "CAP_SYS_NICE", "CAP_SYS_RESOURCE", "CAP_SYS_TIME", "CAP_SYS_TTY_CONFIG", "CAP_MKNOD", "CAP_LEASE", "CAP_AUDIT_WRITE", "CAP_AUDIT_CONTROL", "CAP_SETFCAP"}; static const unsigned int numCaps = sizeof(capabilityIDs) / sizeof(*capabilityIDs); +bool check_capability(const unsigned int cap) +{ + // First assume header version 1 + int capsize = 1; // VFS_CAP_U32_1 + cap_user_data_t data = NULL; + cap_user_header_t hdr = calloc(sizeof(*hdr), capsize); + + // Determine capabilities version used by the current kernel + capget(hdr, NULL); + + // Check version + if (hdr->version != LINUX_CAPABILITY_VERSION_1) + { + // If unknown version, use largest supported version (3) + // Version 2 is deprecated according to linux/capability.h + if (hdr->version != LINUX_CAPABILITY_VERSION_2) + { + hdr->version = LINUX_CAPABILITY_VERSION_3; + capsize = 2; // VFS_CAP_U32_3 + } + else + { + // Use version 2 + capsize = 2; // VFS_CAP_U32_2 + } + } + + // Get current capabilities + data = calloc(sizeof(*data), capsize); + capget(hdr, data); + + // Check if the capability is available + const bool available = ((data->permitted & (1 << cap)) && (data->effective & (1 << cap))); + + // Free memory + free(hdr); + free(data); + + return available; +} + bool check_capabilities(void) { // First assume header version 1 diff --git a/src/capabilities.h b/src/capabilities.h index a65113828..6f811301e 100644 --- a/src/capabilities.h +++ b/src/capabilities.h @@ -10,6 +10,7 @@ #ifndef CAPABILITIES_H #define CAPABILITIES_H +bool check_capability(const unsigned int cap); bool check_capabilities(void); #endif //CAPABILITIES_H diff --git a/src/tools/arp-scan.c b/src/tools/arp-scan.c index c3eef0e81..964c07db5 100644 --- a/src/tools/arp-scan.c +++ b/src/tools/arp-scan.c @@ -16,6 +16,9 @@ #include "dhcp-discover.h" // sleepms() #include "timers.h" +// check_capability() +#include "capabilities.h" +#include #include #include @@ -184,7 +187,9 @@ static int create_arp_socket(const int ifindex, const char *iface, const char ** if(arp_socket < 0) { *error = strerror(errno); +#ifdef DEBUG printf("Unable to create socket for ARP communications on interface %s: %s\n", iface, *error); +#endif return -1; } @@ -196,7 +201,9 @@ static int create_arp_socket(const int ifindex, const char *iface, const char ** if (bind(arp_socket, (struct sockaddr*) &sll, sizeof(struct sockaddr_ll)) < 0) { *error = strerror(errno); +#ifdef DEBUG printf("Unable to bind socket for ARP communications on interface %s: %s\n", iface, *error); +#endif close(arp_socket); return -1; } @@ -208,7 +215,9 @@ static int create_arp_socket(const int ifindex, const char *iface, const char ** if (setsockopt(arp_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) { *error = strerror(errno); +#ifdef DEBUG printf("Unable to set timeout for ARP communications on interface %s: %s\n", iface, *error); +#endif close(arp_socket); return -1; } @@ -530,6 +539,13 @@ static void print_results(struct thread_data *thread_data) int run_arp_scan(const bool scan_all) { + // Check if we are capable of sending ARP packets + if(!check_capability(CAP_NET_RAW)) + { + puts("Error: Insufficient permissions or capabilities (needs CAP_NET_RAW). Try running as root (sudo)"); + return EXIT_FAILURE; + } + arp_all = scan_all; puts("Discovering IPv4 hosts on the network using the Address Resolution Protocol (ARP)...\n"); From 73e3d8840864270bf4d3fcd3244c965045d9ad91 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 17 May 2023 16:53:51 +0200 Subject: [PATCH 43/49] Optimize thread_data structure and store a thread-local copy of the interface name Signed-off-by: DL6ER --- src/tools/arp-scan.c | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/tools/arp-scan.c b/src/tools/arp-scan.c index 964c07db5..3a81c1ebe 100644 --- a/src/tools/arp-scan.c +++ b/src/tools/arp-scan.c @@ -82,23 +82,23 @@ enum status { STATUS_SCANNING, STATUS_ERROR, STATUS_COMPLETE -}; +} __attribute__ ((packed)); struct thread_data { - int dst_cidr; - struct sockaddr_in src_addr; - struct sockaddr_in dst_addr; - struct sockaddr_in mask; - struct ifaddrs *ifa; - const char *iface; - struct arp_result *result; - size_t result_size; - enum status status; + char iface[IF_NAMESIZE + 1]; char ipstr[INET_ADDRSTRLEN]; - unsigned char mac[16]; + unsigned char mac[MAC_LENGTH]; + enum status status; + int dst_cidr; unsigned int num_scans; + size_t result_size; uint32_t scanned_addresses; const char *error; + struct ifaddrs *ifa; + struct arp_result *result; + struct sockaddr_in src_addr; + struct sockaddr_in dst_addr; + struct sockaddr_in mask; }; // Sends multiple ARP who-has request on interface ifindex, using source mac src_mac and source ip src_ip. @@ -488,11 +488,17 @@ static void print_results(struct thread_data *thread_data) // Print MAC addresses for(j = 0; j < MAX_MACS; j++) { + // Check if result[i].mac[j] is all-zero, if so, skip this entry + if(memcmp(thread_data->result[i].device[j].mac, "\x00\x00\x00\x00\x00\x00", 6) == 0) + break; + // Check if IP address replied + replies = 0u; bool replied = false; for(unsigned int k = 0; k < NUM_SCANS; k++) { replied |= thread_data->result[i].device[j].replied[k] > 0; + replies += thread_data->result[i].device[j].replied[k] > 0 ? 1 : 0; multiple_replies += thread_data->result[i].device[j].replied[k] > 1; } if(!replied) @@ -505,9 +511,6 @@ static void print_results(struct thread_data *thread_data) struct in_addr ip = { 0 }; ip.s_addr = htonl(ntohl(thread_data->dst_addr.sin_addr.s_addr) + i); inet_ntop(AF_INET, &ip, thread_data->ipstr, INET_ADDRSTRLEN); - // Check if result[i].mac[j] is all-zero - if(memcmp(thread_data->result[i].device[j].mac, "\x00\x00\x00\x00\x00\x00", 6) == 0) - break; // Print MAC address printf("%-16s %-16s %-24s %02x:%02x:%02x:%02x:%02x:%02x ", @@ -523,7 +526,7 @@ static void print_results(struct thread_data *thread_data) for(unsigned int k = 0; k < NUM_SCANS; k++) printf(" %s", thread_data->result[i].device[j].replied[k] > 0 ? "X" : "-"); - putc('\n', stdout); + printf(" (%u%%)\n", replies * 100 / NUM_SCANS); } // Print warning if we received multiple replies @@ -572,7 +575,7 @@ int run_arp_scan(const bool scan_all) if(tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_INET) { thread_data[tid].ifa = tmp; - thread_data[tid].iface = tmp->ifa_name; + strncpy(thread_data[tid].iface, tmp->ifa_name, sizeof(thread_data[tid].iface) - 1); // Get interface IPv4 address memcpy(&thread_data[tid].src_addr, tmp->ifa_addr, sizeof(thread_data[tid].src_addr)); @@ -643,7 +646,7 @@ int run_arp_scan(const bool scan_all) sleepms(1000); } } - puts("100%\n\n"); + puts("100%\n"); // Wait for all threads to join back with us for(unsigned int i = 0; i < tid; i++) From 6ce6eaeda512ccde3325b3e572667080ba0987aa Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 17 May 2023 17:02:15 +0200 Subject: [PATCH 44/49] Add arp-scan -xtreme mode for very unreliable connections Signed-off-by: DL6ER --- src/args.c | 9 ++++++--- src/tools/arp-scan.c | 34 ++++++++++++++++++---------------- src/tools/arp-scan.h | 2 +- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/args.c b/src/args.c index 14b4031d5..16391ff4f 100644 --- a/src/args.c +++ b/src/args.c @@ -178,8 +178,9 @@ void parse_args(int argc, char* argv[]) { // Enable stdout printing cli_mode = true; - const bool arp_all = argc > 2 && strcmp(argv[2], "-a") == 0; - exit(run_arp_scan(arp_all)); + const bool scan_all = argc > 2 && strcmp(argv[2], "-a") == 0; + const bool extreme_mode = argc > 2 && strcmp(argv[2], "-x") == 0; + exit(run_arp_scan(scan_all, extreme_mode)); } // start from 1, as argv[0] is the executable name @@ -513,10 +514,12 @@ void parse_args(int argc, char* argv[]) printf("%sOther:%s\n", yellow, normal); printf("\t%sdhcp-discover%s Discover DHCP servers in the local\n", green, normal); printf("\t network\n"); - printf("\t%sarp-scan %s[-a]%s Use ARP to scan local network for\n", green, cyan, normal); + printf("\t%sarp-scan %s[-a/-x]%s Use ARP to scan local network for\n", green, cyan, normal); printf("\t possible IP conflicts\n"); printf("\t Append %s-a%s to force scan on all\n", cyan, normal); printf("\t interfaces\n"); + printf("\t Append %s-x%s to force scan on all\n", cyan, normal); + printf("\t interfaces and scan 10x more often\n"); printf("\t%s-h%s, %shelp%s Display this help and exit\n\n", green, normal, green, normal); exit(EXIT_SUCCESS); } diff --git a/src/tools/arp-scan.c b/src/tools/arp-scan.c index 3a81c1ebe..408ae6392 100644 --- a/src/tools/arp-scan.c +++ b/src/tools/arp-scan.c @@ -40,9 +40,6 @@ // How long do we wait for ARP replies in each scan [seconds]? #define ARP_TIMEOUT 1 -// Global constant -static bool arp_all = false; - // Protocol definitions #define PROTO_ARP 0x0806 #define ETH2_HEADER_LEN 14 @@ -71,7 +68,8 @@ struct arp_header { struct arp_result { struct device { - unsigned int replied[NUM_SCANS]; + // In extreme mode, we can scan up to 10x more often + unsigned int replied[10*NUM_SCANS]; unsigned char mac[MAC_LENGTH]; } device[MAX_MACS]; }; @@ -85,12 +83,14 @@ enum status { } __attribute__ ((packed)); struct thread_data { + bool scan_all :1; char iface[IF_NAMESIZE + 1]; char ipstr[INET_ADDRSTRLEN]; unsigned char mac[MAC_LENGTH]; enum status status; int dst_cidr; unsigned int num_scans; + unsigned int total_scans; size_t result_size; uint32_t scanned_addresses; const char *error; @@ -368,7 +368,7 @@ static void *arp_scan_iface(void *args) const int ifindex = if_nametoindex(iface); // Scan only interfaces with CIDR >= 24 - if(thread_data->dst_cidr < 24 && !arp_all) + if(thread_data->dst_cidr < 24 && !thread_data->scan_all) { thread_data->status = STATUS_SKIPPED_CIDR_MISMATCH; #ifdef DEBUG @@ -403,10 +403,10 @@ static void *arp_scan_iface(void *args) struct arp_result *result = calloc(thread_data->result_size, sizeof(struct arp_result)); thread_data->result = result; - for(thread_data->num_scans = 0; thread_data->num_scans < NUM_SCANS; thread_data->num_scans++) + for(thread_data->num_scans = 0; thread_data->num_scans < thread_data->total_scans; thread_data->num_scans++) { #ifdef DEBUG - printf("Still scanning interface %s (%s/%i) %i%%...\n", iface, thread_data->ipstr, thread_data->dst_cidr, 100*scan_id/NUM_SCANS); + printf("Still scanning interface %s (%s/%i) %i%%...\n", iface, thread_data->ipstr, thread_data->dst_cidr, 100*scan_id/thread_data->total_scans); #endif // Send ARP requests to all IPs in subnet if(send_arps(arp_socket, ifindex, iface, thread_data->mac, &thread_data->error, &thread_data->src_addr.sin_addr, @@ -440,7 +440,7 @@ static void print_results(struct thread_data *thread_data) if(thread_data->status == STATUS_SKIPPED_CIDR_MISMATCH) { - printf("Skipped interface %s (%s/%i) because of too large network (use -a to force scanning this interface)\n\n", + printf("Skipped interface %s (%s/%i) because of too large network (use -a or -x to force scanning this interface)\n\n", thread_data->iface, thread_data->ipstr, thread_data->dst_cidr); return; } @@ -457,7 +457,7 @@ static void print_results(struct thread_data *thread_data) unsigned int replies = 0; for(unsigned int i = 0; i < thread_data->result_size; i++) for(unsigned int j = 0; j < MAX_MACS; j++) - for(unsigned int k = 0; k < NUM_SCANS; k++) + for(unsigned int k = 0; k < thread_data->total_scans; k++) replies += thread_data->result[i].device[j].replied[k]; // Exit early if there are no results @@ -476,7 +476,7 @@ static void print_results(struct thread_data *thread_data) // Add our own IP address to the results so IP conflicts can be detected // (our own IP address is not included in the ARP scan) - for(unsigned int i = 0; i < NUM_SCANS; i++) + for(unsigned int i = 0; i < thread_data->total_scans; i++) add_result(thread_data->iface, &thread_data->src_addr.sin_addr, &thread_data->dst_addr.sin_addr, thread_data->mac, thread_data->result, thread_data->result_size, i); // Print results @@ -495,7 +495,7 @@ static void print_results(struct thread_data *thread_data) // Check if IP address replied replies = 0u; bool replied = false; - for(unsigned int k = 0; k < NUM_SCANS; k++) + for(unsigned int k = 0; k < thread_data->total_scans; k++) { replied |= thread_data->result[i].device[j].replied[k] > 0; replies += thread_data->result[i].device[j].replied[k] > 0 ? 1 : 0; @@ -523,10 +523,10 @@ static void print_results(struct thread_data *thread_data) thread_data->result[i].device[j].mac[4], thread_data->result[i].device[j].mac[5]); - for(unsigned int k = 0; k < NUM_SCANS; k++) + for(unsigned int k = 0; k < thread_data->total_scans; k++) printf(" %s", thread_data->result[i].device[j].replied[k] > 0 ? "X" : "-"); - printf(" (%u%%)\n", replies * 100 / NUM_SCANS); + printf(" (%u%%)\n", replies * 100 / thread_data->total_scans); } // Print warning if we received multiple replies @@ -540,7 +540,7 @@ static void print_results(struct thread_data *thread_data) putc('\n', stdout); } -int run_arp_scan(const bool scan_all) +int run_arp_scan(const bool scan_all, const bool extreme_mode) { // Check if we are capable of sending ARP packets if(!check_capability(CAP_NET_RAW)) @@ -549,7 +549,6 @@ int run_arp_scan(const bool scan_all) return EXIT_FAILURE; } - arp_all = scan_all; puts("Discovering IPv4 hosts on the network using the Address Resolution Protocol (ARP)...\n"); // Get interface names for available interfaces on this machine @@ -581,6 +580,9 @@ int run_arp_scan(const bool scan_all) memcpy(&thread_data[tid].src_addr, tmp->ifa_addr, sizeof(thread_data[tid].src_addr)); inet_ntop(AF_INET, &thread_data[tid].src_addr.sin_addr, thread_data[tid].ipstr, INET_ADDRSTRLEN); + thread_data[tid].scan_all = scan_all || extreme_mode; + thread_data[tid].total_scans = extreme_mode ? 10*NUM_SCANS : NUM_SCANS; + // Always skip the loopback interface if(thread_data[tid].src_addr.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) { @@ -620,7 +622,7 @@ int run_arp_scan(const bool scan_all) { // Also add up scans for completed threads num_scans += thread_data[i].scanned_addresses; - total_scans += NUM_SCANS * thread_data[i].result_size; + total_scans += thread_data[i].total_scans * thread_data[i].result_size; } } if(!all_done) diff --git a/src/tools/arp-scan.h b/src/tools/arp-scan.h index 0a5b56711..c22eafd44 100644 --- a/src/tools/arp-scan.h +++ b/src/tools/arp-scan.h @@ -11,6 +11,6 @@ #ifndef ARP_SCAN_H #define ARP_SCAN_H -int run_arp_scan(const bool scan_all); +int run_arp_scan(const bool scan_all, const bool extreme_mode); #endif // ARP_SCAN_H From ba8807cc6e4e61c3de8794e661bbebaed852b5bc Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 17 May 2023 22:24:53 +0200 Subject: [PATCH 45/49] Give reply rate in percent instead of showing the reply matrix Signed-off-by: DL6ER --- src/tools/arp-scan.c | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/tools/arp-scan.c b/src/tools/arp-scan.c index 408ae6392..3fa9ec2ec 100644 --- a/src/tools/arp-scan.c +++ b/src/tools/arp-scan.c @@ -471,8 +471,8 @@ static void print_results(struct thread_data *thread_data) // If there is at least one result, print header printf("ARP scan on interface %s (%s/%i) finished\n", thread_data->iface, thread_data->ipstr, thread_data->dst_cidr); - printf("%-16s %-16s %-24s %-17s Reply matrix\n", - "IP address", "Interface", "Hostname", "MAC address"); + printf("%-16s %-16s %-24s %-17s %s\n", + "IP address", "Interface", "Hostname", "MAC address", "Reply rate"); // Add our own IP address to the results so IP conflicts can be detected // (our own IP address is not included in the ARP scan) @@ -483,7 +483,6 @@ static void print_results(struct thread_data *thread_data) for(unsigned int i = 0; i < thread_data->result_size; i++) { unsigned int j = 0, replied_devices = 0; - unsigned int multiple_replies = 0; // Print MAC addresses for(j = 0; j < MAX_MACS; j++) @@ -495,6 +494,7 @@ static void print_results(struct thread_data *thread_data) // Check if IP address replied replies = 0u; bool replied = false; + unsigned int multiple_replies = 0; for(unsigned int k = 0; k < thread_data->total_scans; k++) { replied |= thread_data->result[i].device[j].replied[k] > 0; @@ -513,7 +513,7 @@ static void print_results(struct thread_data *thread_data) inet_ntop(AF_INET, &ip, thread_data->ipstr, INET_ADDRSTRLEN); // Print MAC address - printf("%-16s %-16s %-24s %02x:%02x:%02x:%02x:%02x:%02x ", + printf("%-16s %-16s %-24s %02x:%02x:%02x:%02x:%02x:%02x %u %%\n", thread_data->ipstr, thread_data->iface, get_hostname(&ip), thread_data->result[i].device[j].mac[0], @@ -521,21 +521,29 @@ static void print_results(struct thread_data *thread_data) thread_data->result[i].device[j].mac[2], thread_data->result[i].device[j].mac[3], thread_data->result[i].device[j].mac[4], - thread_data->result[i].device[j].mac[5]); + thread_data->result[i].device[j].mac[5], + replies * 100 / thread_data->total_scans); +#ifdef DEBUG for(unsigned int k = 0; k < thread_data->total_scans; k++) printf(" %s", thread_data->result[i].device[j].replied[k] > 0 ? "X" : "-"); - - printf(" (%u%%)\n", replies * 100 / thread_data->total_scans); +#endif + if(multiple_replies > 0) + printf("WARNING: Received multiple replies from %02x:%02x:%02x:%02x:%02x:%02x for %s in %i scan%s\n", + thread_data->result[i].device[j].mac[0], + thread_data->result[i].device[j].mac[1], + thread_data->result[i].device[j].mac[2], + thread_data->result[i].device[j].mac[3], + thread_data->result[i].device[j].mac[4], + thread_data->result[i].device[j].mac[5], + thread_data->ipstr, + multiple_replies, multiple_replies > 1 ? "s" : ""); } // Print warning if we received multiple replies if(replied_devices > 1) printf("WARNING: Received replies for %s from %i devices\n", thread_data->ipstr, replied_devices); - if(multiple_replies > 0) - printf("WARNING: Received multiple replies for %s in %i scan%s\n", - thread_data->ipstr, multiple_replies, multiple_replies > 1 ? "s" : ""); } putc('\n', stdout); } @@ -633,7 +641,7 @@ int run_arp_scan(const bool scan_all, const bool extreme_mode) if(new_progress > progress) { // Print progress - printf(" %i%%", new_progress); + printf(" %i%% ", new_progress); // Update progress progress = new_progress; From 615be9c2c9ba60e95378c6c727996fe48687e3c6 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 18 May 2023 09:04:26 +0200 Subject: [PATCH 46/49] Exit early if insufficient memory is available, perform as many interface scans as possible under these conditions Signed-off-by: DL6ER --- src/tools/arp-scan.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/tools/arp-scan.c b/src/tools/arp-scan.c index 3fa9ec2ec..57bd648b5 100644 --- a/src/tools/arp-scan.c +++ b/src/tools/arp-scan.c @@ -399,8 +399,18 @@ static void *arp_scan_iface(void *args) // Allocate memory for ARP response buffer const size_t arp_result_len = 1 << (32 - thread_data->dst_cidr); + struct arp_result *result = calloc(arp_result_len, sizeof(struct arp_result)); + if(result == NULL) + { + // Memory allocation failed due to insufficient memory being + // available + thread_data->status = STATUS_ERROR; + thread_data->error = strerror(ENOMEM); + pthread_exit(NULL); + } + + // Store ARP response buffer in thread_data thread_data->result_size = arp_result_len; - struct arp_result *result = calloc(thread_data->result_size, sizeof(struct arp_result)); thread_data->result = result; for(thread_data->num_scans = 0; thread_data->num_scans < thread_data->total_scans; thread_data->num_scans++) From 4d7640c9bed607dd2888c41a19ff2e9867328e27 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 18 May 2023 09:08:26 +0200 Subject: [PATCH 47/49] Reduce memory requirements by factor 4x Signed-off-by: DL6ER --- src/tools/arp-scan.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tools/arp-scan.c b/src/tools/arp-scan.c index 57bd648b5..4442e8222 100644 --- a/src/tools/arp-scan.c +++ b/src/tools/arp-scan.c @@ -69,11 +69,13 @@ struct arp_header { struct arp_result { struct device { // In extreme mode, we can scan up to 10x more often - unsigned int replied[10*NUM_SCANS]; + unsigned char replied[10*NUM_SCANS]; unsigned char mac[MAC_LENGTH]; } device[MAX_MACS]; }; +const size_t arp_result_size = sizeof(struct arp_result); + enum status { STATUS_INITIALIZING = 0, STATUS_SKIPPED_CIDR_MISMATCH, From d1f70d7d6c1b89266f11a0ac666f8e09ae2b4b33 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 18 May 2023 09:45:24 +0200 Subject: [PATCH 48/49] Further reduce memory requirements by factor 10x (if not in -x mode) Signed-off-by: DL6ER --- src/tools/arp-scan.c | 182 ++++++++++++++++++++++++++----------------- 1 file changed, 111 insertions(+), 71 deletions(-) diff --git a/src/tools/arp-scan.c b/src/tools/arp-scan.c index 4442e8222..14f9c9f01 100644 --- a/src/tools/arp-scan.c +++ b/src/tools/arp-scan.c @@ -68,14 +68,19 @@ struct arp_header { struct arp_result { struct device { + unsigned char replied[NUM_SCANS]; + unsigned char mac[MAC_LENGTH]; + } device[MAX_MACS]; +}; + +struct arp_result_extreme { + struct device_extreme { // In extreme mode, we can scan up to 10x more often unsigned char replied[10*NUM_SCANS]; unsigned char mac[MAC_LENGTH]; } device[MAX_MACS]; }; -const size_t arp_result_size = sizeof(struct arp_result); - enum status { STATUS_INITIALIZING = 0, STATUS_SKIPPED_CIDR_MISMATCH, @@ -86,6 +91,7 @@ enum status { struct thread_data { bool scan_all :1; + bool extreme :1; char iface[IF_NAMESIZE + 1]; char ipstr[INET_ADDRSTRLEN]; unsigned char mac[MAC_LENGTH]; @@ -97,7 +103,10 @@ struct thread_data { uint32_t scanned_addresses; const char *error; struct ifaddrs *ifa; - struct arp_result *result; + union { + struct arp_result_extreme *result_extreme; + struct arp_result *result; + }; struct sockaddr_in src_addr; struct sockaddr_in dst_addr; struct sockaddr_in mask; @@ -105,8 +114,7 @@ struct thread_data { // Sends multiple ARP who-has request on interface ifindex, using source mac src_mac and source ip src_ip. // Iterates over all IP addresses in the range of dst_ip/cidr. -static int send_arps(const int fd, const int ifindex, const char *iface, const unsigned char *src_mac, const char **error, - struct in_addr *src_ip, struct in_addr dst_ip, const int dst_cidr, uint32_t *scanned_addresses) +static int send_arps(const int fd, const int ifindex, struct thread_data *thread_data) { int err = -1; unsigned char buffer[BUF_SIZE]; @@ -134,9 +142,9 @@ static int send_arps(const int fd, const int ifindex, const char *iface, const u memset(arp_req->target_mac, 0x00, MAC_LENGTH); // Source MAC to our own MAC address - memcpy(send_req->h_source, src_mac, MAC_LENGTH); - memcpy(arp_req->sender_mac, src_mac, MAC_LENGTH); - memcpy(socket_address.sll_addr, src_mac, MAC_LENGTH); + memcpy(send_req->h_source, thread_data->mac, MAC_LENGTH); + memcpy(arp_req->sender_mac, thread_data->mac, MAC_LENGTH); + memcpy(socket_address.sll_addr, thread_data->mac, MAC_LENGTH); // Protocol type is ARP send_req->h_proto = htons(ETH_P_ARP); @@ -149,11 +157,12 @@ static int send_arps(const int fd, const int ifindex, const char *iface, const u arp_req->opcode = htons(ARP_REQUEST); // Copy IP address to arp_req - memcpy(arp_req->sender_ip, &src_ip->s_addr, sizeof(src_ip->s_addr)); + memcpy(arp_req->sender_ip, &thread_data->src_addr.sin_addr.s_addr, sizeof(thread_data->src_addr.sin_addr.s_addr)); // Loop over all possible IP addresses in the range dst_ip/cidr // We start at 1 because the first IP address has already been set above - for(unsigned int i = 0; i < (1u << (32 - dst_cidr)); i++) + struct in_addr dst_ip = thread_data->dst_addr.sin_addr; + for(unsigned int i = 0; i < (1u << (32 - thread_data->dst_cidr)); i++) { // Fill in target IP address memcpy(arp_req->target_ip, &dst_ip.s_addr, sizeof(dst_ip.s_addr)); @@ -167,14 +176,14 @@ static int send_arps(const int fd, const int ifindex, const char *iface, const u if (ret == -1) { err = errno; - *error = strerror(err); + thread_data->error = strerror(err); goto out; } // Increment IP address dst_ip.s_addr = htonl(ntohl(dst_ip.s_addr) + 1); - (*scanned_addresses)++; + thread_data->scanned_addresses++; } err = 0; @@ -227,15 +236,16 @@ static int create_arp_socket(const int ifindex, const char *iface, const char ** return arp_socket; } -static void add_result(const char *iface, struct in_addr *rcv_ip, struct in_addr *dst_ip, unsigned char *sender_mac, - struct arp_result *result, const size_t result_len, const unsigned int scan_id) +static void add_result(struct in_addr *rcv_ip, unsigned char *sender_mac, + struct thread_data *thread_data, const unsigned int scan_id) { // Check if we have already found this IP address - uint32_t i = ntohl(rcv_ip->s_addr) - ntohl(dst_ip->s_addr); - if(i >= result_len) + uint32_t i = ntohl(rcv_ip->s_addr) - ntohl(thread_data->dst_addr.sin_addr.s_addr); + if(i >= thread_data->result_size) { - printf("Received IP address %s out of range for interface %s (%u >= %zu)\n", inet_ntoa(*rcv_ip), iface, i, result_len); + printf("Received IP address %s out of range for interface %s (%u >= %zu)\n", + inet_ntoa(*rcv_ip), thread_data->iface, i, thread_data->result_size); return; } @@ -243,27 +253,31 @@ static void add_result(const char *iface, struct in_addr *rcv_ip, struct in_addr unsigned int j = 0; for(; j < MAX_MACS; j++) { + unsigned char *mac = thread_data->extreme ? + thread_data->result_extreme[i].device[j].mac : + thread_data->result[i].device[j].mac; // Check if received MAC is already stored in result[i].device[j].mac - if(memcmp(result[i].device[j].mac, sender_mac, MAC_LENGTH) == 0) + if(memcmp(mac, sender_mac, MAC_LENGTH) == 0) { break; } - // Check if result[i].device[j].mac is all-zero - if(memcmp(result[i].device[j].mac, "\x00\x00\x00\x00\x00\x00", MAC_LENGTH) == 0) + // Check if mac is all-zero + if(memcmp(mac, "\x00\x00\x00\x00\x00\x00", MAC_LENGTH) == 0) { - // Copy MAC address to result[i].device[j].mac - memcpy(result[i].device[j].mac, sender_mac, MAC_LENGTH); + // Copy MAC address to mac + memcpy(mac, sender_mac, MAC_LENGTH); break; } } // Memorize that we have received a reply for this IP address - result[i].device[j].replied[scan_id]++; + thread_data->extreme ? + thread_data->result_extreme[i].device[j].replied[scan_id]++ : + thread_data->result[i].device[j].replied[scan_id]++; } // Read all ARP responses -static ssize_t read_arp(const int fd, const char *iface, struct in_addr *dst_ip, const char **error, - struct arp_result *result, const size_t result_len, const unsigned int scan_id) +static ssize_t read_arp(const int fd, struct thread_data *thread_data) { ssize_t ret = 0; unsigned char buffer[BUF_SIZE]; @@ -282,8 +296,8 @@ static ssize_t read_arp(const int fd, const char *iface, struct in_addr *dst_ip, } // Error - *error = strerror(errno); - printf("recvfrom(): %s", *error); + thread_data->error = strerror(errno); + printf("recvfrom(): %s", thread_data->error); break; } struct ethhdr *rcv_resp = (struct ethhdr *) buffer; @@ -310,7 +324,7 @@ static ssize_t read_arp(const int fd, const char *iface, struct in_addr *dst_ip, #ifdef DEBUG printf("%-16s %-20s\t%02x:%02x:%02x:%02x:%02x:%02x", - iface, inet_ntoa(sender_a), + thread_data->iface, inet_ntoa(sender_a), arp_resp->sender_mac[0], arp_resp->sender_mac[1], arp_resp->sender_mac[2], @@ -318,7 +332,7 @@ static ssize_t read_arp(const int fd, const char *iface, struct in_addr *dst_ip, arp_resp->sender_mac[4], arp_resp->sender_mac[5]); #endif - add_result(iface, &sender_a, dst_ip, arp_resp->sender_mac, result, result_len, scan_id); + add_result(&sender_a, arp_resp->sender_mac, thread_data, thread_data->num_scans); } return ret; @@ -401,19 +415,35 @@ static void *arp_scan_iface(void *args) // Allocate memory for ARP response buffer const size_t arp_result_len = 1 << (32 - thread_data->dst_cidr); - struct arp_result *result = calloc(arp_result_len, sizeof(struct arp_result)); - if(result == NULL) + thread_data->result_size = arp_result_len; + if(thread_data->extreme) { - // Memory allocation failed due to insufficient memory being - // available - thread_data->status = STATUS_ERROR; - thread_data->error = strerror(ENOMEM); - pthread_exit(NULL); + // Allocate extreme memory for ARP response buffer + struct arp_result_extreme *result = calloc(arp_result_len, sizeof(struct arp_result_extreme)); + if(result == NULL) + { + // Memory allocation failed due to insufficient memory being + // available + thread_data->status = STATUS_ERROR; + thread_data->error = strerror(ENOMEM); + pthread_exit(NULL); + } + thread_data->result_extreme = result; + } + else + { + // Allocate memory for ARP response buffer + struct arp_result *result = calloc(arp_result_len, sizeof(struct arp_result)); + if(result == NULL) + { + // Memory allocation failed due to insufficient memory being + // available + thread_data->status = STATUS_ERROR; + thread_data->error = strerror(ENOMEM); + pthread_exit(NULL); + } + thread_data->result = result; } - - // Store ARP response buffer in thread_data - thread_data->result_size = arp_result_len; - thread_data->result = result; for(thread_data->num_scans = 0; thread_data->num_scans < thread_data->total_scans; thread_data->num_scans++) { @@ -421,16 +451,14 @@ static void *arp_scan_iface(void *args) printf("Still scanning interface %s (%s/%i) %i%%...\n", iface, thread_data->ipstr, thread_data->dst_cidr, 100*scan_id/thread_data->total_scans); #endif // Send ARP requests to all IPs in subnet - if(send_arps(arp_socket, ifindex, iface, thread_data->mac, &thread_data->error, &thread_data->src_addr.sin_addr, - thread_data->dst_addr.sin_addr, thread_data->dst_cidr, &thread_data->scanned_addresses) != 0) + if(send_arps(arp_socket, ifindex, thread_data) != 0) { thread_data->status = STATUS_ERROR; break; } // Read ARP responses - if(read_arp(arp_socket, iface, &thread_data->dst_addr.sin_addr, &thread_data->error, - thread_data->result, thread_data->result_size, thread_data->num_scans) != 0) + if(read_arp(arp_socket, thread_data) != 0) { thread_data->status = STATUS_ERROR; break; @@ -466,14 +494,29 @@ static void print_results(struct thread_data *thread_data) } // Check if there are any results - unsigned int replies = 0; + bool any_replies = false; for(unsigned int i = 0; i < thread_data->result_size; i++) for(unsigned int j = 0; j < MAX_MACS; j++) for(unsigned int k = 0; k < thread_data->total_scans; k++) - replies += thread_data->result[i].device[j].replied[k]; + if(thread_data->extreme) + { + if(thread_data->result_extreme[i].device[j].replied[k]) + { + any_replies = true; + break; + } + } + else + { + if(thread_data->result[i].device[j].replied[k]) + { + any_replies = true; + break; + } + } // Exit early if there are no results - if(replies == 0) + if(!any_replies) { printf("No devices replied on interface %s (%s/%i)\n\n", thread_data->iface, thread_data->ipstr, thread_data->dst_cidr); @@ -489,7 +532,7 @@ static void print_results(struct thread_data *thread_data) // Add our own IP address to the results so IP conflicts can be detected // (our own IP address is not included in the ARP scan) for(unsigned int i = 0; i < thread_data->total_scans; i++) - add_result(thread_data->iface, &thread_data->src_addr.sin_addr, &thread_data->dst_addr.sin_addr, thread_data->mac, thread_data->result, thread_data->result_size, i); + add_result(&thread_data->src_addr.sin_addr, thread_data->mac, thread_data, i); // Print results for(unsigned int i = 0; i < thread_data->result_size; i++) @@ -500,18 +543,25 @@ static void print_results(struct thread_data *thread_data) for(j = 0; j < MAX_MACS; j++) { // Check if result[i].mac[j] is all-zero, if so, skip this entry - if(memcmp(thread_data->result[i].device[j].mac, "\x00\x00\x00\x00\x00\x00", 6) == 0) + unsigned char *mac = thread_data->extreme ? + thread_data->result_extreme[i].device[j].mac : + thread_data->result[i].device[j].mac; + if(memcmp(mac, "\x00\x00\x00\x00\x00\x00", 6) == 0) break; - // Check if IP address replied - replies = 0u; bool replied = false; - unsigned int multiple_replies = 0; + unsigned char replies = 0u; + unsigned char multiple_replies = 0; + const unsigned char *rp = thread_data->extreme ? + thread_data->result_extreme[i].device[j].replied : + thread_data->result[i].device[j].replied; + + // Check if IP address replied for(unsigned int k = 0; k < thread_data->total_scans; k++) { - replied |= thread_data->result[i].device[j].replied[k] > 0; - replies += thread_data->result[i].device[j].replied[k] > 0 ? 1 : 0; - multiple_replies += thread_data->result[i].device[j].replied[k] > 1; + replied |= rp[k] > 0; + replies += rp[k] > 0 ? 1 : 0; + multiple_replies += rp[k] > 1; } if(!replied) continue; @@ -528,28 +578,17 @@ static void print_results(struct thread_data *thread_data) printf("%-16s %-16s %-24s %02x:%02x:%02x:%02x:%02x:%02x %u %%\n", thread_data->ipstr, thread_data->iface, get_hostname(&ip), - thread_data->result[i].device[j].mac[0], - thread_data->result[i].device[j].mac[1], - thread_data->result[i].device[j].mac[2], - thread_data->result[i].device[j].mac[3], - thread_data->result[i].device[j].mac[4], - thread_data->result[i].device[j].mac[5], + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], replies * 100 / thread_data->total_scans); #ifdef DEBUG for(unsigned int k = 0; k < thread_data->total_scans; k++) - printf(" %s", thread_data->result[i].device[j].replied[k] > 0 ? "X" : "-"); + printf(" %s", rp[k] > 0 ? "X" : "-"); #endif if(multiple_replies > 0) - printf("WARNING: Received multiple replies from %02x:%02x:%02x:%02x:%02x:%02x for %s in %i scan%s\n", - thread_data->result[i].device[j].mac[0], - thread_data->result[i].device[j].mac[1], - thread_data->result[i].device[j].mac[2], - thread_data->result[i].device[j].mac[3], - thread_data->result[i].device[j].mac[4], - thread_data->result[i].device[j].mac[5], - thread_data->ipstr, - multiple_replies, multiple_replies > 1 ? "s" : ""); + printf("INFO: Received multiple replies from %02x:%02x:%02x:%02x:%02x:%02x for %s in %i scan%s\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], + thread_data->ipstr, multiple_replies, multiple_replies > 1 ? "s" : ""); } // Print warning if we received multiple replies @@ -600,6 +639,7 @@ int run_arp_scan(const bool scan_all, const bool extreme_mode) memcpy(&thread_data[tid].src_addr, tmp->ifa_addr, sizeof(thread_data[tid].src_addr)); inet_ntop(AF_INET, &thread_data[tid].src_addr.sin_addr, thread_data[tid].ipstr, INET_ADDRSTRLEN); + thread_data[tid].extreme = extreme_mode; thread_data[tid].scan_all = scan_all || extreme_mode; thread_data[tid].total_scans = extreme_mode ? 10*NUM_SCANS : NUM_SCANS; From 31a90da5ca7464611b5e406613414488c9457ce3 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 20 May 2023 13:57:20 +0200 Subject: [PATCH 49/49] Align % in reply rate column Signed-off-by: DL6ER --- src/tools/arp-scan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/arp-scan.c b/src/tools/arp-scan.c index 14f9c9f01..46e3496f3 100644 --- a/src/tools/arp-scan.c +++ b/src/tools/arp-scan.c @@ -575,7 +575,7 @@ static void print_results(struct thread_data *thread_data) inet_ntop(AF_INET, &ip, thread_data->ipstr, INET_ADDRSTRLEN); // Print MAC address - printf("%-16s %-16s %-24s %02x:%02x:%02x:%02x:%02x:%02x %u %%\n", + printf("%-16s %-16s %-24s %02x:%02x:%02x:%02x:%02x:%02x %3u %%\n", thread_data->ipstr, thread_data->iface, get_hostname(&ip), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],