From noreply at nginx.com Tue Oct 1 14:00:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 1 Oct 2024 14:00:02 +0000 (UTC) Subject: [nginx] SSL: caching CRLs. Message-ID: <20241001140002.9C9A34777F@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/61314518de74fcb3af954ea6e6cb2820307676d0 branches: master commit: 61314518de74fcb3af954ea6e6cb2820307676d0 user: Sergey Kandaurov date: Mon, 9 Sep 2024 19:05:31 +0400 description: SSL: caching CRLs. Based on previous work by Mini Hawthorne. --- src/event/ngx_event_openssl.c | 72 ++++++++++++++++++------ src/event/ngx_event_openssl.h | 1 + src/event/ngx_event_openssl_cache.c | 108 +++++++++++++++++++++++++++++++++++- 3 files changed, 164 insertions(+), 17 deletions(-) diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index 1a6ca18ad..72189e1a4 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -18,6 +18,7 @@ typedef struct { } ngx_openssl_conf_t; +static ngx_inline ngx_int_t ngx_ssl_cert_already_in_hash(void); static int ngx_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store); static void ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where, int ret); @@ -739,17 +740,16 @@ ngx_ssl_trusted_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, ngx_int_t ngx_ssl_crl(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *crl) { - X509_STORE *store; - X509_LOOKUP *lookup; + int n, i; + char *err; + X509_CRL *x509; + X509_STORE *store; + STACK_OF(X509_CRL) *chain; if (crl->len == 0) { return NGX_OK; } - if (ngx_conf_full_name(cf->cycle, crl, 1) != NGX_OK) { - return NGX_ERROR; - } - store = SSL_CTX_get_cert_store(ssl->ctx); if (store == NULL) { @@ -758,22 +758,36 @@ ngx_ssl_crl(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *crl) return NGX_ERROR; } - lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); + chain = ngx_ssl_cache_fetch(cf, NGX_SSL_CACHE_CRL, &err, crl, NULL); + if (chain == NULL) { + if (err != NULL) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "cannot load CRL \"%s\": %s", crl->data, err); + } - if (lookup == NULL) { - ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, - "X509_STORE_add_lookup() failed"); return NGX_ERROR; } - if (X509_LOOKUP_load_file(lookup, (char *) crl->data, X509_FILETYPE_PEM) - == 0) - { - ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, - "X509_LOOKUP_load_file(\"%s\") failed", crl->data); - return NGX_ERROR; + n = sk_X509_CRL_num(chain); + + for (i = 0; i < n; i++) { + x509 = sk_X509_CRL_value(chain, i); + + if (X509_STORE_add_crl(store, x509) != 1) { + + if (ngx_ssl_cert_already_in_hash()) { + continue; + } + + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "X509_STORE_add_crl(\"%s\") failed", crl->data); + sk_X509_CRL_pop_free(chain, X509_CRL_free); + return NGX_ERROR; + } } + sk_X509_CRL_pop_free(chain, X509_CRL_free); + X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL); @@ -781,6 +795,32 @@ ngx_ssl_crl(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *crl) } +static ngx_inline ngx_int_t +ngx_ssl_cert_already_in_hash(void) +{ +#if !(OPENSSL_VERSION_NUMBER >= 0x1010009fL \ + || LIBRESSL_VERSION_NUMBER >= 0x3050000fL) + u_long error; + + /* + * OpenSSL prior to 1.1.0i doesn't ignore duplicate certificate entries, + * see https://github.com/openssl/openssl/commit/c0452248 + */ + + error = ERR_peek_last_error(); + + if (ERR_GET_LIB(error) == ERR_LIB_X509 + && ERR_GET_REASON(error) == X509_R_CERT_ALREADY_IN_HASH_TABLE) + { + ERR_clear_error(); + return 1; + } +#endif + + return 0; +} + + static int ngx_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store) { diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h index 5e36fb5e0..e2f0e5821 100644 --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -195,6 +195,7 @@ typedef struct { #define NGX_SSL_CACHE_CERT 0 #define NGX_SSL_CACHE_PKEY 1 +#define NGX_SSL_CACHE_CRL 2 ngx_int_t ngx_ssl_init(ngx_log_t *log); diff --git a/src/event/ngx_event_openssl_cache.c b/src/event/ngx_event_openssl_cache.c index f5bb9a241..b2615d2bf 100644 --- a/src/event/ngx_event_openssl_cache.c +++ b/src/event/ngx_event_openssl_cache.c @@ -65,6 +65,11 @@ static int ngx_ssl_cache_pkey_password_callback(char *buf, int size, int rwflag, static void ngx_ssl_cache_pkey_free(void *data); static void *ngx_ssl_cache_pkey_ref(char **err, void *data); +static void *ngx_ssl_cache_crl_create(ngx_ssl_cache_key_t *id, char **err, + void *data); +static void ngx_ssl_cache_crl_free(void *data); +static void *ngx_ssl_cache_crl_ref(char **err, void *data); + static BIO *ngx_ssl_cache_create_bio(ngx_ssl_cache_key_t *id, char **err); static void *ngx_openssl_cache_create_conf(ngx_cycle_t *cycle); @@ -107,6 +112,11 @@ static ngx_ssl_cache_type_t ngx_ssl_cache_types[] = { { ngx_ssl_cache_pkey_create, ngx_ssl_cache_pkey_free, ngx_ssl_cache_pkey_ref }, + + /* NGX_SSL_CACHE_CRL */ + { ngx_ssl_cache_crl_create, + ngx_ssl_cache_crl_free, + ngx_ssl_cache_crl_ref }, }; @@ -177,7 +187,9 @@ static ngx_int_t ngx_ssl_cache_init_key(ngx_pool_t *pool, ngx_uint_t index, ngx_str_t *path, ngx_ssl_cache_key_t *id) { - if (ngx_strncmp(path->data, "data:", sizeof("data:") - 1) == 0) { + if (index <= NGX_SSL_CACHE_PKEY + && ngx_strncmp(path->data, "data:", sizeof("data:") - 1) == 0) + { id->type = NGX_SSL_CACHE_DATA; } else if (index == NGX_SSL_CACHE_PKEY @@ -517,6 +529,100 @@ ngx_ssl_cache_pkey_ref(char **err, void *data) } +static void * +ngx_ssl_cache_crl_create(ngx_ssl_cache_key_t *id, char **err, void *data) +{ + BIO *bio; + u_long n; + X509_CRL *x509; + STACK_OF(X509_CRL) *chain; + + chain = sk_X509_CRL_new_null(); + if (chain == NULL) { + *err = "sk_X509_CRL_new_null() failed"; + return NULL; + } + + bio = ngx_ssl_cache_create_bio(id, err); + if (bio == NULL) { + sk_X509_CRL_pop_free(chain, X509_CRL_free); + return NULL; + } + + for ( ;; ) { + + x509 = PEM_read_bio_X509_CRL(bio, NULL, NULL, NULL); + if (x509 == NULL) { + n = ERR_peek_last_error(); + + if (ERR_GET_LIB(n) == ERR_LIB_PEM + && ERR_GET_REASON(n) == PEM_R_NO_START_LINE + && sk_X509_CRL_num(chain) > 0) + { + /* end of file */ + ERR_clear_error(); + break; + } + + /* some real error */ + + *err = "PEM_read_bio_X509_CRL() failed"; + BIO_free(bio); + sk_X509_CRL_pop_free(chain, X509_CRL_free); + return NULL; + } + + if (sk_X509_CRL_push(chain, x509) == 0) { + *err = "sk_X509_CRL_push() failed"; + BIO_free(bio); + X509_CRL_free(x509); + sk_X509_CRL_pop_free(chain, X509_CRL_free); + return NULL; + } + } + + BIO_free(bio); + + return chain; +} + + +static void +ngx_ssl_cache_crl_free(void *data) +{ + sk_X509_CRL_pop_free(data, X509_CRL_free); +} + + +static void * +ngx_ssl_cache_crl_ref(char **err, void *data) +{ + int n, i; + X509_CRL *x509; + STACK_OF(X509_CRL) *chain; + + chain = sk_X509_CRL_dup(data); + if (chain == NULL) { + *err = "sk_X509_CRL_dup() failed"; + return NULL; + } + + n = sk_X509_CRL_num(chain); + + for (i = 0; i < n; i++) { + x509 = sk_X509_CRL_value(chain, i); + +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) + X509_CRL_up_ref(x509); +#else + CRYPTO_add(&x509->references, 1, CRYPTO_LOCK_X509_CRL); +#endif + } + + return chain; +} + + static BIO * ngx_ssl_cache_create_bio(ngx_ssl_cache_key_t *id, char **err) { From noreply at nginx.com Tue Oct 1 14:00:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 1 Oct 2024 14:00:02 +0000 (UTC) Subject: [nginx] SSL: caching certificates. Message-ID: <20241001140002.8F0A14777D@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/78ed123e71aab17ffecfa0b2b27a349cfb4b2502 branches: master commit: 78ed123e71aab17ffecfa0b2b27a349cfb4b2502 user: Sergey Kandaurov date: Mon, 9 Sep 2024 19:03:52 +0400 description: SSL: caching certificates. Certificate chains are now loaded once. The certificate cache provides each chain as a unique stack of reference counted elements. This shallow copy is required because OpenSSL stacks aren't reference counted. Based on previous work by Mini Hawthorne. --- src/event/ngx_event_openssl.c | 105 ++--------------------- src/event/ngx_event_openssl.h | 3 + src/event/ngx_event_openssl_cache.c | 167 ++++++++++++++++++++++++++++++++++-- 3 files changed, 173 insertions(+), 102 deletions(-) diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index 3ed003062..018d03016 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -18,8 +18,6 @@ typedef struct { } ngx_openssl_conf_t; -static X509 *ngx_ssl_load_certificate(ngx_pool_t *pool, char **err, - ngx_str_t *cert, STACK_OF(X509) **chain); static EVP_PKEY *ngx_ssl_load_certificate_key(ngx_pool_t *pool, char **err, ngx_str_t *key, ngx_array_t *passwords); static int ngx_ssl_password_callback(char *buf, int size, int rwflag, @@ -449,8 +447,8 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, EVP_PKEY *pkey; STACK_OF(X509) *chain; - x509 = ngx_ssl_load_certificate(cf->pool, &err, cert, &chain); - if (x509 == NULL) { + chain = ngx_ssl_cache_fetch(cf, NGX_SSL_CACHE_CERT, &err, cert, NULL); + if (chain == NULL) { if (err != NULL) { ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "cannot load certificate \"%s\": %s", @@ -460,6 +458,8 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, return NGX_ERROR; } + x509 = sk_X509_shift(chain); + if (SSL_CTX_use_certificate(ssl->ctx, x509) == 0) { ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "SSL_CTX_use_certificate(\"%s\") failed", cert->data); @@ -570,8 +570,9 @@ ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool, EVP_PKEY *pkey; STACK_OF(X509) *chain; - x509 = ngx_ssl_load_certificate(pool, &err, cert, &chain); - if (x509 == NULL) { + chain = ngx_ssl_cache_connection_fetch(pool, NGX_SSL_CACHE_CERT, &err, + cert, NULL); + if (chain == NULL) { if (err != NULL) { ngx_ssl_error(NGX_LOG_ERR, c->log, 0, "cannot load certificate \"%s\": %s", @@ -581,6 +582,8 @@ ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool, return NGX_ERROR; } + x509 = sk_X509_shift(chain); + if (SSL_use_certificate(c->ssl->connection, x509) == 0) { ngx_ssl_error(NGX_LOG_ERR, c->log, 0, "SSL_use_certificate(\"%s\") failed", cert->data); @@ -632,96 +635,6 @@ ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool, } -static X509 * -ngx_ssl_load_certificate(ngx_pool_t *pool, char **err, ngx_str_t *cert, - STACK_OF(X509) **chain) -{ - BIO *bio; - X509 *x509, *temp; - u_long n; - - if (ngx_strncmp(cert->data, "data:", sizeof("data:") - 1) == 0) { - - bio = BIO_new_mem_buf(cert->data + sizeof("data:") - 1, - cert->len - (sizeof("data:") - 1)); - if (bio == NULL) { - *err = "BIO_new_mem_buf() failed"; - return NULL; - } - - } else { - - if (ngx_get_full_name(pool, (ngx_str_t *) &ngx_cycle->conf_prefix, cert) - != NGX_OK) - { - *err = NULL; - return NULL; - } - - bio = BIO_new_file((char *) cert->data, "r"); - if (bio == NULL) { - *err = "BIO_new_file() failed"; - return NULL; - } - } - - /* certificate itself */ - - x509 = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL); - if (x509 == NULL) { - *err = "PEM_read_bio_X509_AUX() failed"; - BIO_free(bio); - return NULL; - } - - /* rest of the chain */ - - *chain = sk_X509_new_null(); - if (*chain == NULL) { - *err = "sk_X509_new_null() failed"; - BIO_free(bio); - X509_free(x509); - return NULL; - } - - for ( ;; ) { - - temp = PEM_read_bio_X509(bio, NULL, NULL, NULL); - if (temp == NULL) { - n = ERR_peek_last_error(); - - if (ERR_GET_LIB(n) == ERR_LIB_PEM - && ERR_GET_REASON(n) == PEM_R_NO_START_LINE) - { - /* end of file */ - ERR_clear_error(); - break; - } - - /* some real error */ - - *err = "PEM_read_bio_X509() failed"; - BIO_free(bio); - X509_free(x509); - sk_X509_pop_free(*chain, X509_free); - return NULL; - } - - if (sk_X509_push(*chain, temp) == 0) { - *err = "sk_X509_push() failed"; - BIO_free(bio); - X509_free(x509); - sk_X509_pop_free(*chain, X509_free); - return NULL; - } - } - - BIO_free(bio); - - return x509; -} - - static EVP_PKEY * ngx_ssl_load_certificate_key(ngx_pool_t *pool, char **err, ngx_str_t *key, ngx_array_t *passwords) diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h index fa2282ce8..addc7b4d3 100644 --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -193,6 +193,9 @@ typedef struct { #define NGX_SSL_BUFSIZE 16384 +#define NGX_SSL_CACHE_CERT 0 + + ngx_int_t ngx_ssl_init(ngx_log_t *log); ngx_int_t ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data); diff --git a/src/event/ngx_event_openssl_cache.c b/src/event/ngx_event_openssl_cache.c index 98867de50..20b87b79a 100644 --- a/src/event/ngx_event_openssl_cache.c +++ b/src/event/ngx_event_openssl_cache.c @@ -10,6 +10,7 @@ #define NGX_SSL_CACHE_PATH 0 +#define NGX_SSL_CACHE_DATA 1 typedef struct { @@ -51,6 +52,13 @@ static ngx_int_t ngx_ssl_cache_init_key(ngx_pool_t *pool, ngx_uint_t index, static ngx_ssl_cache_node_t *ngx_ssl_cache_lookup(ngx_ssl_cache_t *cache, ngx_ssl_cache_type_t *type, ngx_ssl_cache_key_t *id, uint32_t hash); +static void *ngx_ssl_cache_cert_create(ngx_ssl_cache_key_t *id, char **err, + void *data); +static void ngx_ssl_cache_cert_free(void *data); +static void *ngx_ssl_cache_cert_ref(char **err, void *data); + +static BIO *ngx_ssl_cache_create_bio(ngx_ssl_cache_key_t *id, char **err); + static void *ngx_openssl_cache_create_conf(ngx_cycle_t *cycle); static void ngx_ssl_cache_cleanup(void *data); static void ngx_ssl_cache_node_insert(ngx_rbtree_node_t *temp, @@ -82,6 +90,10 @@ ngx_module_t ngx_openssl_cache_module = { static ngx_ssl_cache_type_t ngx_ssl_cache_types[] = { + /* NGX_SSL_CACHE_CERT */ + { ngx_ssl_cache_cert_create, + ngx_ssl_cache_cert_free, + ngx_ssl_cache_cert_ref }, }; @@ -152,13 +164,18 @@ static ngx_int_t ngx_ssl_cache_init_key(ngx_pool_t *pool, ngx_uint_t index, ngx_str_t *path, ngx_ssl_cache_key_t *id) { - if (ngx_get_full_name(pool, (ngx_str_t *) &ngx_cycle->conf_prefix, path) - != NGX_OK) - { - return NGX_ERROR; - } + if (ngx_strncmp(path->data, "data:", sizeof("data:") - 1) == 0) { + id->type = NGX_SSL_CACHE_DATA; + + } else { + if (ngx_get_full_name(pool, (ngx_str_t *) &ngx_cycle->conf_prefix, path) + != NGX_OK) + { + return NGX_ERROR; + } - id->type = NGX_SSL_CACHE_PATH; + id->type = NGX_SSL_CACHE_PATH; + } id->len = path->len; id->data = path->data; @@ -219,6 +236,144 @@ ngx_ssl_cache_lookup(ngx_ssl_cache_t *cache, ngx_ssl_cache_type_t *type, } +static void * +ngx_ssl_cache_cert_create(ngx_ssl_cache_key_t *id, char **err, void *data) +{ + BIO *bio; + X509 *x509; + u_long n; + STACK_OF(X509) *chain; + + chain = sk_X509_new_null(); + if (chain == NULL) { + *err = "sk_X509_new_null() failed"; + return NULL; + } + + bio = ngx_ssl_cache_create_bio(id, err); + if (bio == NULL) { + sk_X509_pop_free(chain, X509_free); + return NULL; + } + + /* certificate itself */ + + x509 = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL); + if (x509 == NULL) { + *err = "PEM_read_bio_X509_AUX() failed"; + BIO_free(bio); + sk_X509_pop_free(chain, X509_free); + return NULL; + } + + if (sk_X509_push(chain, x509) == 0) { + *err = "sk_X509_push() failed"; + BIO_free(bio); + X509_free(x509); + sk_X509_pop_free(chain, X509_free); + return NULL; + } + + /* rest of the chain */ + + for ( ;; ) { + + x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL); + if (x509 == NULL) { + n = ERR_peek_last_error(); + + if (ERR_GET_LIB(n) == ERR_LIB_PEM + && ERR_GET_REASON(n) == PEM_R_NO_START_LINE) + { + /* end of file */ + ERR_clear_error(); + break; + } + + /* some real error */ + + *err = "PEM_read_bio_X509() failed"; + BIO_free(bio); + sk_X509_pop_free(chain, X509_free); + return NULL; + } + + if (sk_X509_push(chain, x509) == 0) { + *err = "sk_X509_push() failed"; + BIO_free(bio); + X509_free(x509); + sk_X509_pop_free(chain, X509_free); + return NULL; + } + } + + BIO_free(bio); + + return chain; +} + + +static void +ngx_ssl_cache_cert_free(void *data) +{ + sk_X509_pop_free(data, X509_free); +} + + +static void * +ngx_ssl_cache_cert_ref(char **err, void *data) +{ + int n, i; + X509 *x509; + STACK_OF(X509) *chain; + + chain = sk_X509_dup(data); + if (chain == NULL) { + *err = "sk_X509_dup() failed"; + return NULL; + } + + n = sk_X509_num(chain); + + for (i = 0; i < n; i++) { + x509 = sk_X509_value(chain, i); + +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) + X509_up_ref(x509); +#else + CRYPTO_add(&x509->references, 1, CRYPTO_LOCK_X509); +#endif + } + + return chain; +} + + +static BIO * +ngx_ssl_cache_create_bio(ngx_ssl_cache_key_t *id, char **err) +{ + BIO *bio; + + if (id->type == NGX_SSL_CACHE_DATA) { + + bio = BIO_new_mem_buf(id->data + sizeof("data:") - 1, + id->len - (sizeof("data:") - 1)); + if (bio == NULL) { + *err = "BIO_new_mem_buf() failed"; + } + + return bio; + } + + bio = BIO_new_file((char *) id->data, "r"); + if (bio == NULL) { + *err = "BIO_new_file() failed"; + } + + return bio; +} + + static void * ngx_openssl_cache_create_conf(ngx_cycle_t *cycle) { From noreply at nginx.com Tue Oct 1 14:00:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 1 Oct 2024 14:00:02 +0000 (UTC) Subject: [nginx] SSL: caching certificate keys. Message-ID: <20241001140002.9625F4777E@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/7ea2fb6cb197925c7c0e35def9ece12d11b09bb9 branches: master commit: 7ea2fb6cb197925c7c0e35def9ece12d11b09bb9 user: Sergey Kandaurov date: Mon, 9 Sep 2024 19:04:18 +0400 description: SSL: caching certificate keys. EVP_KEY objects are a reference-counted container for key material, shallow copies and OpenSSL stack management aren't needed as with certificates. Based on previous work by Mini Hawthorne. --- src/event/ngx_event_openssl.c | 154 +-------------------------------- src/event/ngx_event_openssl.h | 1 + src/event/ngx_event_openssl_cache.c | 168 ++++++++++++++++++++++++++++++++++++ 3 files changed, 172 insertions(+), 151 deletions(-) diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index 018d03016..1a6ca18ad 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -18,10 +18,6 @@ typedef struct { } ngx_openssl_conf_t; -static EVP_PKEY *ngx_ssl_load_certificate_key(ngx_pool_t *pool, char **err, - ngx_str_t *key, ngx_array_t *passwords); -static int ngx_ssl_password_callback(char *buf, int size, int rwflag, - void *userdata); static int ngx_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store); static void ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where, int ret); @@ -537,7 +533,7 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, } #endif - pkey = ngx_ssl_load_certificate_key(cf->pool, &err, key, passwords); + pkey = ngx_ssl_cache_fetch(cf, NGX_SSL_CACHE_PKEY, &err, key, passwords); if (pkey == NULL) { if (err != NULL) { ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, @@ -611,7 +607,8 @@ ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool, #endif - pkey = ngx_ssl_load_certificate_key(pool, &err, key, passwords); + pkey = ngx_ssl_cache_connection_fetch(pool, NGX_SSL_CACHE_PKEY, &err, + key, passwords); if (pkey == NULL) { if (err != NULL) { ngx_ssl_error(NGX_LOG_ERR, c->log, 0, @@ -635,151 +632,6 @@ ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool, } -static EVP_PKEY * -ngx_ssl_load_certificate_key(ngx_pool_t *pool, char **err, - ngx_str_t *key, ngx_array_t *passwords) -{ - BIO *bio; - EVP_PKEY *pkey; - ngx_str_t *pwd; - ngx_uint_t tries; - pem_password_cb *cb; - - if (ngx_strncmp(key->data, "engine:", sizeof("engine:") - 1) == 0) { - -#ifndef OPENSSL_NO_ENGINE - - u_char *p, *last; - ENGINE *engine; - - p = key->data + sizeof("engine:") - 1; - last = (u_char *) ngx_strchr(p, ':'); - - if (last == NULL) { - *err = "invalid syntax"; - return NULL; - } - - *last = '\0'; - - engine = ENGINE_by_id((char *) p); - - *last++ = ':'; - - if (engine == NULL) { - *err = "ENGINE_by_id() failed"; - return NULL; - } - - pkey = ENGINE_load_private_key(engine, (char *) last, 0, 0); - - if (pkey == NULL) { - *err = "ENGINE_load_private_key() failed"; - ENGINE_free(engine); - return NULL; - } - - ENGINE_free(engine); - - return pkey; - -#else - - *err = "loading \"engine:...\" certificate keys is not supported"; - return NULL; - -#endif - } - - if (ngx_strncmp(key->data, "data:", sizeof("data:") - 1) == 0) { - - bio = BIO_new_mem_buf(key->data + sizeof("data:") - 1, - key->len - (sizeof("data:") - 1)); - if (bio == NULL) { - *err = "BIO_new_mem_buf() failed"; - return NULL; - } - - } else { - - if (ngx_get_full_name(pool, (ngx_str_t *) &ngx_cycle->conf_prefix, key) - != NGX_OK) - { - *err = NULL; - return NULL; - } - - bio = BIO_new_file((char *) key->data, "r"); - if (bio == NULL) { - *err = "BIO_new_file() failed"; - return NULL; - } - } - - if (passwords) { - tries = passwords->nelts; - pwd = passwords->elts; - cb = ngx_ssl_password_callback; - - } else { - tries = 1; - pwd = NULL; - cb = NULL; - } - - for ( ;; ) { - - pkey = PEM_read_bio_PrivateKey(bio, NULL, cb, pwd); - if (pkey != NULL) { - break; - } - - if (tries-- > 1) { - ERR_clear_error(); - (void) BIO_reset(bio); - pwd++; - continue; - } - - *err = "PEM_read_bio_PrivateKey() failed"; - BIO_free(bio); - return NULL; - } - - BIO_free(bio); - - return pkey; -} - - -static int -ngx_ssl_password_callback(char *buf, int size, int rwflag, void *userdata) -{ - ngx_str_t *pwd = userdata; - - if (rwflag) { - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, - "ngx_ssl_password_callback() is called for encryption"); - return 0; - } - - if (pwd == NULL) { - return 0; - } - - if (pwd->len > (size_t) size) { - ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, - "password is truncated to %d bytes", size); - } else { - size = pwd->len; - } - - ngx_memcpy(buf, pwd->data, size); - - return size; -} - - ngx_int_t ngx_ssl_ciphers(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *ciphers, ngx_uint_t prefer_server_ciphers) diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h index addc7b4d3..5e36fb5e0 100644 --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -194,6 +194,7 @@ typedef struct { #define NGX_SSL_CACHE_CERT 0 +#define NGX_SSL_CACHE_PKEY 1 ngx_int_t ngx_ssl_init(ngx_log_t *log); diff --git a/src/event/ngx_event_openssl_cache.c b/src/event/ngx_event_openssl_cache.c index 20b87b79a..f5bb9a241 100644 --- a/src/event/ngx_event_openssl_cache.c +++ b/src/event/ngx_event_openssl_cache.c @@ -11,6 +11,7 @@ #define NGX_SSL_CACHE_PATH 0 #define NGX_SSL_CACHE_DATA 1 +#define NGX_SSL_CACHE_ENGINE 2 typedef struct { @@ -57,6 +58,13 @@ static void *ngx_ssl_cache_cert_create(ngx_ssl_cache_key_t *id, char **err, static void ngx_ssl_cache_cert_free(void *data); static void *ngx_ssl_cache_cert_ref(char **err, void *data); +static void *ngx_ssl_cache_pkey_create(ngx_ssl_cache_key_t *id, char **err, + void *data); +static int ngx_ssl_cache_pkey_password_callback(char *buf, int size, int rwflag, + void *userdata); +static void ngx_ssl_cache_pkey_free(void *data); +static void *ngx_ssl_cache_pkey_ref(char **err, void *data); + static BIO *ngx_ssl_cache_create_bio(ngx_ssl_cache_key_t *id, char **err); static void *ngx_openssl_cache_create_conf(ngx_cycle_t *cycle); @@ -94,6 +102,11 @@ static ngx_ssl_cache_type_t ngx_ssl_cache_types[] = { { ngx_ssl_cache_cert_create, ngx_ssl_cache_cert_free, ngx_ssl_cache_cert_ref }, + + /* NGX_SSL_CACHE_PKEY */ + { ngx_ssl_cache_pkey_create, + ngx_ssl_cache_pkey_free, + ngx_ssl_cache_pkey_ref }, }; @@ -167,6 +180,11 @@ ngx_ssl_cache_init_key(ngx_pool_t *pool, ngx_uint_t index, ngx_str_t *path, if (ngx_strncmp(path->data, "data:", sizeof("data:") - 1) == 0) { id->type = NGX_SSL_CACHE_DATA; + } else if (index == NGX_SSL_CACHE_PKEY + && ngx_strncmp(path->data, "engine:", sizeof("engine:") - 1) == 0) + { + id->type = NGX_SSL_CACHE_ENGINE; + } else { if (ngx_get_full_name(pool, (ngx_str_t *) &ngx_cycle->conf_prefix, path) != NGX_OK) @@ -349,6 +367,156 @@ ngx_ssl_cache_cert_ref(char **err, void *data) } +static void * +ngx_ssl_cache_pkey_create(ngx_ssl_cache_key_t *id, char **err, void *data) +{ + ngx_array_t *passwords = data; + + BIO *bio; + EVP_PKEY *pkey; + ngx_str_t *pwd; + ngx_uint_t tries; + pem_password_cb *cb; + + if (id->type == NGX_SSL_CACHE_ENGINE) { + +#ifndef OPENSSL_NO_ENGINE + + u_char *p, *last; + ENGINE *engine; + + p = id->data + sizeof("engine:") - 1; + last = (u_char *) ngx_strchr(p, ':'); + + if (last == NULL) { + *err = "invalid syntax"; + return NULL; + } + + *last = '\0'; + + engine = ENGINE_by_id((char *) p); + + *last++ = ':'; + + if (engine == NULL) { + *err = "ENGINE_by_id() failed"; + return NULL; + } + + pkey = ENGINE_load_private_key(engine, (char *) last, 0, 0); + + if (pkey == NULL) { + *err = "ENGINE_load_private_key() failed"; + ENGINE_free(engine); + return NULL; + } + + ENGINE_free(engine); + + return pkey; + +#else + + *err = "loading \"engine:...\" certificate keys is not supported"; + return NULL; + +#endif + } + + bio = ngx_ssl_cache_create_bio(id, err); + if (bio == NULL) { + return NULL; + } + + if (passwords) { + tries = passwords->nelts; + pwd = passwords->elts; + cb = ngx_ssl_cache_pkey_password_callback; + + } else { + tries = 1; + pwd = NULL; + cb = NULL; + } + + for ( ;; ) { + + pkey = PEM_read_bio_PrivateKey(bio, NULL, cb, pwd); + if (pkey != NULL) { + break; + } + + if (tries-- > 1) { + ERR_clear_error(); + (void) BIO_reset(bio); + pwd++; + continue; + } + + *err = "PEM_read_bio_PrivateKey() failed"; + BIO_free(bio); + return NULL; + } + + BIO_free(bio); + + return pkey; +} + + +static int +ngx_ssl_cache_pkey_password_callback(char *buf, int size, int rwflag, + void *userdata) +{ + ngx_str_t *pwd = userdata; + + if (rwflag) { + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, + "ngx_ssl_cache_pkey_password_callback() is called " + "for encryption"); + return 0; + } + + if (pwd == NULL) { + return 0; + } + + if (pwd->len > (size_t) size) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, + "password is truncated to %d bytes", size); + } else { + size = pwd->len; + } + + ngx_memcpy(buf, pwd->data, size); + + return size; +} + + +static void +ngx_ssl_cache_pkey_free(void *data) +{ + EVP_PKEY_free(data); +} + + +static void * +ngx_ssl_cache_pkey_ref(char **err, void *data) +{ + EVP_PKEY *pkey = data; + +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) + EVP_PKEY_up_ref(pkey); +#else + CRYPTO_add(&pkey->references, 1, CRYPTO_LOCK_EVP_PKEY); +#endif + + return data; +} + + static BIO * ngx_ssl_cache_create_bio(ngx_ssl_cache_key_t *id, char **err) { From noreply at nginx.com Tue Oct 1 14:00:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 1 Oct 2024 14:00:02 +0000 (UTC) Subject: [nginx] SSL: moved certificate storage out of exdata. Message-ID: <20241001140002.83DBA4777B@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/f36ff3550a7271a618edb119f064dddd086cc380 branches: master commit: f36ff3550a7271a618edb119f064dddd086cc380 user: Sergey Kandaurov date: Mon, 9 Sep 2024 19:02:27 +0400 description: SSL: moved certificate storage out of exdata. Instead of cross-linking the objects using exdata, pointers to configured certificates are now stored in ngx_ssl_t, and OCSP staples are now accessed with rbtree in it. This allows sharing these objects between SSL contexts. Based on previous work by Mini Hawthorne. --- src/event/ngx_event_openssl.c | 78 +++++++++++++--------------------- src/event/ngx_event_openssl.h | 9 ++-- src/event/ngx_event_openssl_stapling.c | 68 ++++++++++++++++++++++------- 3 files changed, 89 insertions(+), 66 deletions(-) diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index 8d1f5695c..3ed003062 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -131,10 +131,8 @@ int ngx_ssl_server_conf_index; int ngx_ssl_session_cache_index; int ngx_ssl_ticket_keys_index; int ngx_ssl_ocsp_index; -int ngx_ssl_certificate_index; -int ngx_ssl_next_certificate_index; +int ngx_ssl_index; int ngx_ssl_certificate_name_index; -int ngx_ssl_stapling_index; ngx_int_t @@ -258,21 +256,14 @@ ngx_ssl_init(ngx_log_t *log) return NGX_ERROR; } - ngx_ssl_certificate_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, - NULL); - if (ngx_ssl_certificate_index == -1) { + ngx_ssl_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL); + + if (ngx_ssl_index == -1) { ngx_ssl_error(NGX_LOG_ALERT, log, 0, "SSL_CTX_get_ex_new_index() failed"); return NGX_ERROR; } - ngx_ssl_next_certificate_index = X509_get_ex_new_index(0, NULL, NULL, NULL, - NULL); - if (ngx_ssl_next_certificate_index == -1) { - ngx_ssl_error(NGX_LOG_ALERT, log, 0, "X509_get_ex_new_index() failed"); - return NGX_ERROR; - } - ngx_ssl_certificate_name_index = X509_get_ex_new_index(0, NULL, NULL, NULL, NULL); @@ -281,13 +272,6 @@ ngx_ssl_init(ngx_log_t *log) return NGX_ERROR; } - ngx_ssl_stapling_index = X509_get_ex_new_index(0, NULL, NULL, NULL, NULL); - - if (ngx_ssl_stapling_index == -1) { - ngx_ssl_error(NGX_LOG_ALERT, log, 0, "X509_get_ex_new_index() failed"); - return NGX_ERROR; - } - return NGX_OK; } @@ -308,12 +292,15 @@ ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data) return NGX_ERROR; } - if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_certificate_index, NULL) == 0) { + if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_index, ssl) == 0) { ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "SSL_CTX_set_ex_data() failed"); return NGX_ERROR; } + ngx_rbtree_init(&ssl->staple_rbtree, &ssl->staple_sentinel, + ngx_rbtree_insert_value); + ssl->buffer_size = NGX_SSL_BUFSIZE; /* client side options */ @@ -458,7 +445,7 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords) { char *err; - X509 *x509; + X509 *x509, **elm; EVP_PKEY *pkey; STACK_OF(X509) *chain; @@ -490,29 +477,29 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, return NGX_ERROR; } - if (X509_set_ex_data(x509, ngx_ssl_next_certificate_index, - SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index)) - == 0) - { - ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "X509_set_ex_data() failed"); - X509_free(x509); - sk_X509_pop_free(chain, X509_free); - return NGX_ERROR; + if (ssl->certs.elts == NULL) { + if (ngx_array_init(&ssl->certs, cf->pool, 1, sizeof(X509 *)) + != NGX_OK) + { + X509_free(x509); + sk_X509_pop_free(chain, X509_free); + return NGX_ERROR; + } } - if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_certificate_index, x509) == 0) { - ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, - "SSL_CTX_set_ex_data() failed"); + elm = ngx_array_push(&ssl->certs); + if (elm == NULL) { X509_free(x509); sk_X509_pop_free(chain, X509_free); return NGX_ERROR; } + *elm = x509; + /* * Note that x509 is not freed here, but will be instead freed in * ngx_ssl_cleanup_ctx(). This is because we need to preserve all - * certificates to be able to iterate all of them through exdata - * (ngx_ssl_certificate_index, ngx_ssl_next_certificate_index), + * certificates to be able to iterate all of them through ssl->certs, * while OpenSSL can free a certificate if it is replaced with another * certificate of the same type. */ @@ -3820,10 +3807,9 @@ ngx_ssl_session_id_context(ngx_ssl_t *ssl, ngx_str_t *sess_ctx, goto failed; } - for (cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index); - cert; - cert = X509_get_ex_data(cert, ngx_ssl_next_certificate_index)) - { + for (k = 0; k < ssl->certs.nelts; k++) { + cert = ((X509 **) ssl->certs.elts)[k]; + if (X509_digest(cert, EVP_sha1(), buf, &len) == 0) { ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "X509_digest() failed"); @@ -3837,9 +3823,7 @@ ngx_ssl_session_id_context(ngx_ssl_t *ssl, ngx_str_t *sess_ctx, } } - if (SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index) == NULL - && certificates != NULL) - { + if (ssl->certs.nelts == 0 && certificates != NULL) { /* * If certificates are loaded dynamically, we use certificate * names as specified in the configuration (with variables). @@ -4851,14 +4835,12 @@ ngx_ssl_cleanup_ctx(void *data) { ngx_ssl_t *ssl = data; - X509 *cert, *next; - - cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index); + X509 *cert; + ngx_uint_t i; - while (cert) { - next = X509_get_ex_data(cert, ngx_ssl_next_certificate_index); + for (i = 0; i < ssl->certs.nelts; i++) { + cert = ((X509 **) ssl->certs.elts)[i]; X509_free(cert); - cert = next; } SSL_CTX_free(ssl->ctx); diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h index ebb2c35bf..41566422a 100644 --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -90,6 +90,11 @@ struct ngx_ssl_s { SSL_CTX *ctx; ngx_log_t *log; size_t buffer_size; + + ngx_array_t certs; + + ngx_rbtree_t staple_rbtree; + ngx_rbtree_node_t staple_sentinel; }; @@ -330,10 +335,8 @@ extern int ngx_ssl_server_conf_index; extern int ngx_ssl_session_cache_index; extern int ngx_ssl_ticket_keys_index; extern int ngx_ssl_ocsp_index; -extern int ngx_ssl_certificate_index; -extern int ngx_ssl_next_certificate_index; +extern int ngx_ssl_index; extern int ngx_ssl_certificate_name_index; -extern int ngx_ssl_stapling_index; #endif /* _NGX_EVENT_OPENSSL_H_INCLUDED_ */ diff --git a/src/event/ngx_event_openssl_stapling.c b/src/event/ngx_event_openssl_stapling.c index e9bb8354e..a0a8031c7 100644 --- a/src/event/ngx_event_openssl_stapling.c +++ b/src/event/ngx_event_openssl_stapling.c @@ -15,6 +15,8 @@ typedef struct { + ngx_rbtree_node_t node; + ngx_str_t staple; ngx_msec_t timeout; @@ -154,6 +156,7 @@ static ngx_int_t ngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl, static int ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn, void *data); +static ngx_ssl_stapling_t *ngx_ssl_stapling_lookup(ngx_ssl_t *ssl, X509 *cert); static void ngx_ssl_stapling_update(ngx_ssl_stapling_t *staple); static void ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx); @@ -195,12 +198,12 @@ ngx_int_t ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file, ngx_str_t *responder, ngx_uint_t verify) { - X509 *cert; + X509 *cert; + ngx_uint_t k; + + for (k = 0; k < ssl->certs.nelts; k++) { + cert = ((X509 **) ssl->certs.elts)[k]; - for (cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index); - cert; - cert = X509_get_ex_data(cert, ngx_ssl_next_certificate_index)) - { if (ngx_ssl_stapling_certificate(cf, ssl, cert, file, responder, verify) != NGX_OK) { @@ -235,10 +238,9 @@ ngx_ssl_stapling_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, X509 *cert, cln->handler = ngx_ssl_stapling_cleanup; cln->data = staple; - if (X509_set_ex_data(cert, ngx_ssl_stapling_index, staple) == 0) { - ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "X509_set_ex_data() failed"); - return NGX_ERROR; - } + staple->node.key = (ngx_rbtree_key_t) cert; + + ngx_rbtree_insert(&ssl->staple_rbtree, &staple->node); #ifdef SSL_CTRL_SELECT_CURRENT_CERT /* OpenSSL 1.0.2+ */ @@ -545,14 +547,21 @@ ngx_int_t ngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_resolver_t *resolver, ngx_msec_t resolver_timeout) { - X509 *cert; + ngx_rbtree_t *tree; + ngx_rbtree_node_t *node; ngx_ssl_stapling_t *staple; - for (cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index); - cert; - cert = X509_get_ex_data(cert, ngx_ssl_next_certificate_index)) + tree = &ssl->staple_rbtree; + + if (tree->root == tree->sentinel) { + return NGX_OK; + } + + for (node = ngx_rbtree_min(tree->root, tree->sentinel); + node; + node = ngx_rbtree_next(tree, node)) { - staple = X509_get_ex_data(cert, ngx_ssl_stapling_index); + staple = ngx_rbtree_data(node, ngx_ssl_stapling_t, node); staple->resolver = resolver; staple->resolver_timeout = resolver_timeout; } @@ -567,6 +576,8 @@ ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn, void *data) int rc; X509 *cert; u_char *p; + SSL_CTX *ssl_ctx; + ngx_ssl_t *ssl; ngx_connection_t *c; ngx_ssl_stapling_t *staple; @@ -583,7 +594,10 @@ ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn, void *data) return rc; } - staple = X509_get_ex_data(cert, ngx_ssl_stapling_index); + ssl_ctx = SSL_get_SSL_CTX(ssl_conn); + ssl = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_index); + + staple = ngx_ssl_stapling_lookup(ssl, cert); if (staple == NULL) { return rc; @@ -613,6 +627,30 @@ ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn, void *data) } +static ngx_ssl_stapling_t * +ngx_ssl_stapling_lookup(ngx_ssl_t *ssl, X509 *cert) +{ + ngx_rbtree_key_t key; + ngx_rbtree_node_t *node, *sentinel; + + node = ssl->staple_rbtree.root; + sentinel = ssl->staple_rbtree.sentinel; + key = (ngx_rbtree_key_t) cert; + + while (node != sentinel) { + + if (key != node->key) { + node = (key < node->key) ? node->left : node->right; + continue; + } + + return ngx_rbtree_data(node, ngx_ssl_stapling_t, node); + } + + return NULL; +} + + static void ngx_ssl_stapling_update(ngx_ssl_stapling_t *staple) { From noreply at nginx.com Tue Oct 1 14:00:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 1 Oct 2024 14:00:02 +0000 (UTC) Subject: [nginx] SSL: object caching. Message-ID: <20241001140002.883794777C@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/7d7e8d2cb8d16e409e0d4c777b30f1d8d7838c7b branches: master commit: 7d7e8d2cb8d16e409e0d4c777b30f1d8d7838c7b user: Sergey Kandaurov date: Mon, 9 Sep 2024 19:03:20 +0400 description: SSL: object caching. Added ngx_openssl_cache_module, which indexes a type-aware object cache. It maps an id to a unique instance, and provides references to it, which are dropped when the cycle's pool is destroyed. The cache will be used in subsequent patches. Based on previous work by Mini Hawthorne. --- auto/modules | 3 +- src/event/ngx_event_openssl.h | 5 + src/event/ngx_event_openssl_cache.c | 311 ++++++++++++++++++++++++++++++++++++ 3 files changed, 318 insertions(+), 1 deletion(-) diff --git a/auto/modules b/auto/modules index 1a5e4212d..38b3aba78 100644 --- a/auto/modules +++ b/auto/modules @@ -1307,10 +1307,11 @@ fi if [ $USE_OPENSSL = YES ]; then ngx_module_type=CORE - ngx_module_name=ngx_openssl_module + ngx_module_name="ngx_openssl_module ngx_openssl_cache_module" ngx_module_incs= ngx_module_deps=src/event/ngx_event_openssl.h ngx_module_srcs="src/event/ngx_event_openssl.c + src/event/ngx_event_openssl_cache.c src/event/ngx_event_openssl_stapling.c" ngx_module_libs= ngx_module_link=YES diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h index 41566422a..fa2282ce8 100644 --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -224,6 +224,11 @@ ngx_int_t ngx_ssl_ocsp_get_status(ngx_connection_t *c, const char **s); void ngx_ssl_ocsp_cleanup(ngx_connection_t *c); ngx_int_t ngx_ssl_ocsp_cache_init(ngx_shm_zone_t *shm_zone, void *data); +void *ngx_ssl_cache_fetch(ngx_conf_t *cf, ngx_uint_t index, char **err, + ngx_str_t *path, void *data); +void *ngx_ssl_cache_connection_fetch(ngx_pool_t *pool, ngx_uint_t index, + char **err, ngx_str_t *path, void *data); + ngx_array_t *ngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file); ngx_array_t *ngx_ssl_preserve_passwords(ngx_conf_t *cf, ngx_array_t *passwords); diff --git a/src/event/ngx_event_openssl_cache.c b/src/event/ngx_event_openssl_cache.c new file mode 100644 index 000000000..98867de50 --- /dev/null +++ b/src/event/ngx_event_openssl_cache.c @@ -0,0 +1,311 @@ + +/* + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +#define NGX_SSL_CACHE_PATH 0 + + +typedef struct { + unsigned type:2; + unsigned len:30; + u_char *data; +} ngx_ssl_cache_key_t; + + +typedef void *(*ngx_ssl_cache_create_pt)(ngx_ssl_cache_key_t *id, char **err, + void *data); +typedef void (*ngx_ssl_cache_free_pt)(void *data); +typedef void *(*ngx_ssl_cache_ref_pt)(char **err, void *data); + + +typedef struct { + ngx_ssl_cache_create_pt create; + ngx_ssl_cache_free_pt free; + ngx_ssl_cache_ref_pt ref; +} ngx_ssl_cache_type_t; + + +typedef struct { + ngx_rbtree_node_t node; + ngx_ssl_cache_key_t id; + ngx_ssl_cache_type_t *type; + void *value; +} ngx_ssl_cache_node_t; + + +typedef struct { + ngx_rbtree_t rbtree; + ngx_rbtree_node_t sentinel; +} ngx_ssl_cache_t; + + +static ngx_int_t ngx_ssl_cache_init_key(ngx_pool_t *pool, ngx_uint_t index, + ngx_str_t *path, ngx_ssl_cache_key_t *id); +static ngx_ssl_cache_node_t *ngx_ssl_cache_lookup(ngx_ssl_cache_t *cache, + ngx_ssl_cache_type_t *type, ngx_ssl_cache_key_t *id, uint32_t hash); + +static void *ngx_openssl_cache_create_conf(ngx_cycle_t *cycle); +static void ngx_ssl_cache_cleanup(void *data); +static void ngx_ssl_cache_node_insert(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); + + +static ngx_core_module_t ngx_openssl_cache_module_ctx = { + ngx_string("openssl_cache"), + ngx_openssl_cache_create_conf, + NULL +}; + + +ngx_module_t ngx_openssl_cache_module = { + NGX_MODULE_V1, + &ngx_openssl_cache_module_ctx, /* module context */ + NULL, /* module directives */ + NGX_CORE_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_ssl_cache_type_t ngx_ssl_cache_types[] = { + +}; + + +void * +ngx_ssl_cache_fetch(ngx_conf_t *cf, ngx_uint_t index, char **err, + ngx_str_t *path, void *data) +{ + uint32_t hash; + ngx_ssl_cache_t *cache; + ngx_ssl_cache_key_t id; + ngx_ssl_cache_type_t *type; + ngx_ssl_cache_node_t *cn; + + if (ngx_ssl_cache_init_key(cf->pool, index, path, &id) != NGX_OK) { + return NULL; + } + + cache = (ngx_ssl_cache_t *) ngx_get_conf(cf->cycle->conf_ctx, + ngx_openssl_cache_module); + + type = &ngx_ssl_cache_types[index]; + hash = ngx_murmur_hash2(id.data, id.len); + + cn = ngx_ssl_cache_lookup(cache, type, &id, hash); + if (cn != NULL) { + return type->ref(err, cn->value); + } + + cn = ngx_palloc(cf->pool, sizeof(ngx_ssl_cache_node_t) + id.len + 1); + if (cn == NULL) { + return NULL; + } + + cn->node.key = hash; + cn->id.data = (u_char *)(cn + 1); + cn->id.len = id.len; + cn->id.type = id.type; + cn->type = type; + + ngx_cpystrn(cn->id.data, id.data, id.len + 1); + + cn->value = type->create(&id, err, data); + if (cn->value == NULL) { + return NULL; + } + + ngx_rbtree_insert(&cache->rbtree, &cn->node); + + return type->ref(err, cn->value); +} + + +void * +ngx_ssl_cache_connection_fetch(ngx_pool_t *pool, ngx_uint_t index, char **err, + ngx_str_t *path, void *data) +{ + ngx_ssl_cache_key_t id; + + if (ngx_ssl_cache_init_key(pool, index, path, &id) != NGX_OK) { + return NULL; + } + + return ngx_ssl_cache_types[index].create(&id, err, data); +} + + +static ngx_int_t +ngx_ssl_cache_init_key(ngx_pool_t *pool, ngx_uint_t index, ngx_str_t *path, + ngx_ssl_cache_key_t *id) +{ + if (ngx_get_full_name(pool, (ngx_str_t *) &ngx_cycle->conf_prefix, path) + != NGX_OK) + { + return NGX_ERROR; + } + + id->type = NGX_SSL_CACHE_PATH; + + id->len = path->len; + id->data = path->data; + + return NGX_OK; +} + + +static ngx_ssl_cache_node_t * +ngx_ssl_cache_lookup(ngx_ssl_cache_t *cache, ngx_ssl_cache_type_t *type, + ngx_ssl_cache_key_t *id, uint32_t hash) +{ + ngx_int_t rc; + ngx_rbtree_node_t *node, *sentinel; + ngx_ssl_cache_node_t *cn; + + node = cache->rbtree.root; + sentinel = cache->rbtree.sentinel; + + while (node != sentinel) { + + if (hash < node->key) { + node = node->left; + continue; + } + + if (hash > node->key) { + node = node->right; + continue; + } + + /* hash == node->key */ + + cn = (ngx_ssl_cache_node_t *) node; + + if (type < cn->type) { + node = node->left; + continue; + } + + if (type > cn->type) { + node = node->right; + continue; + } + + /* type == cn->type */ + + rc = ngx_memn2cmp(id->data, cn->id.data, id->len, cn->id.len); + + if (rc == 0) { + return cn; + } + + node = (rc < 0) ? node->left : node->right; + } + + return NULL; +} + + +static void * +ngx_openssl_cache_create_conf(ngx_cycle_t *cycle) +{ + ngx_ssl_cache_t *cache; + ngx_pool_cleanup_t *cln; + + cache = ngx_pcalloc(cycle->pool, sizeof(ngx_ssl_cache_t)); + if (cache == NULL) { + return NULL; + } + + cln = ngx_pool_cleanup_add(cycle->pool, 0); + if (cln == NULL) { + return NULL; + } + + cln->handler = ngx_ssl_cache_cleanup; + cln->data = cache; + + ngx_rbtree_init(&cache->rbtree, &cache->sentinel, + ngx_ssl_cache_node_insert); + + return cache; +} + + +static void +ngx_ssl_cache_cleanup(void *data) +{ + ngx_ssl_cache_t *cache = data; + + ngx_rbtree_t *tree; + ngx_rbtree_node_t *node; + ngx_ssl_cache_node_t *cn; + + tree = &cache->rbtree; + + if (tree->root == tree->sentinel) { + return; + } + + for (node = ngx_rbtree_min(tree->root, tree->sentinel); + node; + node = ngx_rbtree_next(tree, node)) + { + cn = ngx_rbtree_data(node, ngx_ssl_cache_node_t, node); + cn->type->free(cn->value); + } +} + + +static void +ngx_ssl_cache_node_insert(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) +{ + ngx_rbtree_node_t **p; + ngx_ssl_cache_node_t *n, *t; + + for ( ;; ) { + + n = ngx_rbtree_data(node, ngx_ssl_cache_node_t, node); + t = ngx_rbtree_data(temp, ngx_ssl_cache_node_t, node); + + if (node->key != temp->key) { + + p = (node->key < temp->key) ? &temp->left : &temp->right; + + } else if (n->type != t->type) { + + p = (n->type < t->type) ? &temp->left : &temp->right; + + } else { + + p = (ngx_memn2cmp(n->id.data, t->id.data, n->id.len, t->id.len) + < 0) ? &temp->left : &temp->right; + } + + if (*p == sentinel) { + break; + } + + temp = *p; + } + + *p = node; + node->parent = temp; + node->left = sentinel; + node->right = sentinel; + ngx_rbt_red(node); +} From noreply at nginx.com Tue Oct 1 14:00:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 1 Oct 2024 14:00:02 +0000 (UTC) Subject: [nginx] SSL: caching CA certificates. Message-ID: <20241001140002.A360547780@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/5917e9de5a45bb7288c1c433db840d1a4c6290f3 branches: master commit: 5917e9de5a45bb7288c1c433db840d1a4c6290f3 user: Sergey Kandaurov date: Mon, 9 Sep 2024 19:05:58 +0400 description: SSL: caching CA certificates. This can potentially provide a large amount of savings, because CA certificates can be quite large. Based on previous work by Mini Hawthorne. --- src/event/ngx_event_openssl.c | 146 +++++++++++++++++++++++++++++------- src/event/ngx_event_openssl.h | 1 + src/event/ngx_event_openssl_cache.c | 66 ++++++++++++++++ 3 files changed, 185 insertions(+), 28 deletions(-) diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index 72189e1a4..2b1d107df 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -22,6 +22,8 @@ static ngx_inline ngx_int_t ngx_ssl_cert_already_in_hash(void); static int ngx_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store); static void ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where, int ret); +static int ngx_ssl_cmp_x509_name(const X509_NAME *const *a, + const X509_NAME *const *b); static void ngx_ssl_passwords_cleanup(void *data); static int ngx_ssl_new_client_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess); @@ -656,6 +658,12 @@ ngx_int_t ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, ngx_int_t depth) { + int n, i; + char *err; + X509 *x509; + X509_NAME *name; + X509_STORE *store; + STACK_OF(X509) *chain; STACK_OF(X509_NAME) *list; SSL_CTX_set_verify(ssl->ctx, SSL_VERIFY_PEER, ngx_ssl_verify_callback); @@ -666,34 +674,84 @@ ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, return NGX_OK; } - if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) { + list = sk_X509_NAME_new(ngx_ssl_cmp_x509_name); + if (list == NULL) { return NGX_ERROR; } - if (SSL_CTX_load_verify_locations(ssl->ctx, (char *) cert->data, NULL) - == 0) - { + store = SSL_CTX_get_cert_store(ssl->ctx); + + if (store == NULL) { ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, - "SSL_CTX_load_verify_locations(\"%s\") failed", - cert->data); + "SSL_CTX_get_cert_store() failed"); return NGX_ERROR; } - /* - * SSL_CTX_load_verify_locations() may leave errors in the error queue - * while returning success - */ + chain = ngx_ssl_cache_fetch(cf, NGX_SSL_CACHE_CA, &err, cert, NULL); + if (chain == NULL) { + if (err != NULL) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "cannot load certificate \"%s\": %s", + cert->data, err); + } - ERR_clear_error(); + sk_X509_NAME_pop_free(list, X509_NAME_free); + return NGX_ERROR; + } - list = SSL_load_client_CA_file((char *) cert->data); + n = sk_X509_num(chain); - if (list == NULL) { - ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, - "SSL_load_client_CA_file(\"%s\") failed", cert->data); - return NGX_ERROR; + for (i = 0; i < n; i++) { + x509 = sk_X509_value(chain, i); + + if (X509_STORE_add_cert(store, x509) != 1) { + + if (ngx_ssl_cert_already_in_hash()) { + continue; + } + + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "X509_STORE_add_cert(\"%s\") failed", cert->data); + sk_X509_NAME_pop_free(list, X509_NAME_free); + sk_X509_pop_free(chain, X509_free); + return NGX_ERROR; + } + + name = X509_get_subject_name(x509); + if (name == NULL) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "X509_get_subject_name(\"%s\") failed", cert->data); + sk_X509_NAME_pop_free(list, X509_NAME_free); + sk_X509_pop_free(chain, X509_free); + return NGX_ERROR; + } + + name = X509_NAME_dup(name); + if (name == NULL) { + sk_X509_NAME_pop_free(list, X509_NAME_free); + sk_X509_pop_free(chain, X509_free); + return NGX_ERROR; + } + +#ifdef OPENSSL_IS_BORINGSSL + if (sk_X509_NAME_find(list, NULL, name) > 0) { +#else + if (sk_X509_NAME_find(list, name) >= 0) { +#endif + X509_NAME_free(name); + continue; + } + + if (sk_X509_NAME_push(list, name) == 0) { + sk_X509_NAME_pop_free(list, X509_NAME_free); + sk_X509_pop_free(chain, X509_free); + X509_NAME_free(name); + return NGX_ERROR; + } } + sk_X509_pop_free(chain, X509_free); + SSL_CTX_set_client_CA_list(ssl->ctx, list); return NGX_OK; @@ -704,6 +762,12 @@ ngx_int_t ngx_ssl_trusted_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, ngx_int_t depth) { + int i, n; + char *err; + X509 *x509; + X509_STORE *store; + STACK_OF(X509) *chain; + SSL_CTX_set_verify(ssl->ctx, SSL_CTX_get_verify_mode(ssl->ctx), ngx_ssl_verify_callback); @@ -713,25 +777,44 @@ ngx_ssl_trusted_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, return NGX_OK; } - if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) { + store = SSL_CTX_get_cert_store(ssl->ctx); + + if (store == NULL) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "SSL_CTX_get_cert_store() failed"); return NGX_ERROR; } - if (SSL_CTX_load_verify_locations(ssl->ctx, (char *) cert->data, NULL) - == 0) - { - ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, - "SSL_CTX_load_verify_locations(\"%s\") failed", - cert->data); + chain = ngx_ssl_cache_fetch(cf, NGX_SSL_CACHE_CA, &err, cert, NULL); + if (chain == NULL) { + if (err != NULL) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "cannot load certificate \"%s\": %s", + cert->data, err); + } + return NGX_ERROR; } - /* - * SSL_CTX_load_verify_locations() may leave errors in the error queue - * while returning success - */ + n = sk_X509_num(chain); - ERR_clear_error(); + for (i = 0; i < n; i++) { + x509 = sk_X509_value(chain, i); + + if (X509_STORE_add_cert(store, x509) != 1) { + + if (ngx_ssl_cert_already_in_hash()) { + continue; + } + + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "X509_STORE_add_cert(\"%s\") failed", cert->data); + sk_X509_pop_free(chain, X509_free); + return NGX_ERROR; + } + } + + sk_X509_pop_free(chain, X509_free); return NGX_OK; } @@ -987,6 +1070,13 @@ ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where, int ret) } +static int +ngx_ssl_cmp_x509_name(const X509_NAME *const *a, const X509_NAME *const *b) +{ + return (X509_NAME_cmp(*a, *b)); +} + + ngx_array_t * ngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file) { diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h index e2f0e5821..6d171229c 100644 --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -196,6 +196,7 @@ typedef struct { #define NGX_SSL_CACHE_CERT 0 #define NGX_SSL_CACHE_PKEY 1 #define NGX_SSL_CACHE_CRL 2 +#define NGX_SSL_CACHE_CA 3 ngx_int_t ngx_ssl_init(ngx_log_t *log); diff --git a/src/event/ngx_event_openssl_cache.c b/src/event/ngx_event_openssl_cache.c index b2615d2bf..f43bdb5e7 100644 --- a/src/event/ngx_event_openssl_cache.c +++ b/src/event/ngx_event_openssl_cache.c @@ -70,6 +70,9 @@ static void *ngx_ssl_cache_crl_create(ngx_ssl_cache_key_t *id, char **err, static void ngx_ssl_cache_crl_free(void *data); static void *ngx_ssl_cache_crl_ref(char **err, void *data); +static void *ngx_ssl_cache_ca_create(ngx_ssl_cache_key_t *id, char **err, + void *data); + static BIO *ngx_ssl_cache_create_bio(ngx_ssl_cache_key_t *id, char **err); static void *ngx_openssl_cache_create_conf(ngx_cycle_t *cycle); @@ -117,6 +120,11 @@ static ngx_ssl_cache_type_t ngx_ssl_cache_types[] = { { ngx_ssl_cache_crl_create, ngx_ssl_cache_crl_free, ngx_ssl_cache_crl_ref }, + + /* NGX_SSL_CACHE_CA */ + { ngx_ssl_cache_ca_create, + ngx_ssl_cache_cert_free, + ngx_ssl_cache_cert_ref } }; @@ -623,6 +631,64 @@ ngx_ssl_cache_crl_ref(char **err, void *data) } +static void * +ngx_ssl_cache_ca_create(ngx_ssl_cache_key_t *id, char **err, void *data) +{ + BIO *bio; + X509 *x509; + u_long n; + STACK_OF(X509) *chain; + + chain = sk_X509_new_null(); + if (chain == NULL) { + *err = "sk_X509_new_null() failed"; + return NULL; + } + + bio = ngx_ssl_cache_create_bio(id, err); + if (bio == NULL) { + sk_X509_pop_free(chain, X509_free); + return NULL; + } + + for ( ;; ) { + + x509 = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL); + if (x509 == NULL) { + n = ERR_peek_last_error(); + + if (ERR_GET_LIB(n) == ERR_LIB_PEM + && ERR_GET_REASON(n) == PEM_R_NO_START_LINE + && sk_X509_num(chain) > 0) + { + /* end of file */ + ERR_clear_error(); + break; + } + + /* some real error */ + + *err = "PEM_read_bio_X509_AUX() failed"; + BIO_free(bio); + sk_X509_pop_free(chain, X509_free); + return NULL; + } + + if (sk_X509_push(chain, x509) == 0) { + *err = "sk_X509_push() failed"; + BIO_free(bio); + X509_free(x509); + sk_X509_pop_free(chain, X509_free); + return NULL; + } + } + + BIO_free(bio); + + return chain; +} + + static BIO * ngx_ssl_cache_create_bio(ngx_ssl_cache_key_t *id, char **err) { From noreply at nginx.com Tue Oct 1 23:31:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 1 Oct 2024 23:31:02 +0000 (UTC) Subject: [njs] Version 0.8.6. Message-ID: <20241001233102.6C97247782@pubserv1.nginx> details: https://github.com/nginx/njs/commit/c5a29a7af8894ee1ec44ebda71ef0ea1f2a31af6 branches: master commit: c5a29a7af8894ee1ec44ebda71ef0ea1f2a31af6 user: Dmitry Volyntsev date: Thu, 26 Sep 2024 17:47:07 -0700 description: Version 0.8.6. --- CHANGES | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/CHANGES b/CHANGES index 32eb840f..859ae077 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,41 @@ +Changes with njs 0.8.6 02 Oct 2024 + + nginx modules: + + *) Feature: introduced QuickJS engine. + + *) Feature: added optional nocache flag for js_set directive. + Thanks to Thomas P. + + *) Feature: exposed capture group variables in HTTP module. + Thanks to Thomas P. + + Core: + + *) Feature: added Buffer module for QuickJS engine. + + *) Bugfix: fixed handling of empty labelled statement in a function. + + *) Bugfix: fixed Function constructor handling when called without + arguments. + + *) Bugfix: fixed Buffer.prototype.writeInt8() and friends. + + *) Bugfix: fixed Buffer.prototype.writeFloat() and friends. + + *) Bugfix: fixed Buffer.prototype.lastIndexOf(). + + *) Bugfix: fixed Buffer.prototype.write(). + + *) Bugfix: fixed maybe-uninitialized warnings in error creation. + + *) Bugfix: fixed 'ctx.codepoint' initialization in UTF-8 decoding. + + *) Bugfix: fixed 'length' initialization in Array.prototype.pop(). + + *) Bugfix: fixed handling of encode arg in fs.readdir() and + fs.realpath(). + Changes with njs 0.8.5 25 Jun 2024 nginx modules: From noreply at nginx.com Tue Oct 1 23:38:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 1 Oct 2024 23:38:02 +0000 (UTC) Subject: [njs] Annotated tag created: 0.8.6 Message-ID: <20241001233802.683A847784@pubserv1.nginx> details: https://github.com/nginx/njs/releases/tag/0.8.6 branches: commit: c5a29a7af8894ee1ec44ebda71ef0ea1f2a31af6 user: Dmitry Volyntsev date: Tue Oct 1 16:36:28 2024 -0700 description: Version 0.8.6. From noreply at nginx.com Wed Oct 2 15:14:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Wed, 2 Oct 2024 15:14:02 +0000 (UTC) Subject: [nginx] Updated OpenSSL used for win32 builds. Message-ID: <20241002151402.E65DD477AD@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/0e7c9ddb275a496b24adc3f1de8eebe70a40ee26 branches: master commit: 0e7c9ddb275a496b24adc3f1de8eebe70a40ee26 user: Sergey Kandaurov date: Tue, 1 Oct 2024 19:23:45 +0400 description: Updated OpenSSL used for win32 builds. --- misc/GNUmakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/GNUmakefile b/misc/GNUmakefile index fcf34ec34..e18a86a63 100644 --- a/misc/GNUmakefile +++ b/misc/GNUmakefile @@ -6,7 +6,7 @@ TEMP = tmp CC = cl OBJS = objs.msvc8 -OPENSSL = openssl-3.0.14 +OPENSSL = openssl-3.0.15 ZLIB = zlib-1.3.1 PCRE = pcre2-10.39 From noreply at nginx.com Wed Oct 2 15:14:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Wed, 2 Oct 2024 15:14:02 +0000 (UTC) Subject: [nginx] nginx-1.27.2-RELEASE Message-ID: <20241002151402.F1F14477AE@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/e24f7ccc161f1a2a759eb27263ec9af4fc7c8e96 branches: master commit: e24f7ccc161f1a2a759eb27263ec9af4fc7c8e96 user: Sergey Kandaurov date: Tue, 1 Oct 2024 23:59:48 +0400 description: nginx-1.27.2-RELEASE --- docs/xml/nginx/changes.xml | 65 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml index 9c2f317bc..c18dedff4 100644 --- a/docs/xml/nginx/changes.xml +++ b/docs/xml/nginx/changes.xml @@ -5,6 +5,71 @@ + + + + +SSL-сертификаты, секретные ключи и списки CRL теперь кешируются +на старте или во время переконфигурации. + + +SSL certificates, secret keys, and CRLs are now cached +on start or during reconfiguration. + + + + + +проверка клиентских сертификатов с помощью OCSP в модуле stream. + + +client certificate validation with OCSP in the stream module. + + + + + +поддержка OCSP stapling в модуле stream. + + +OCSP stapling support in the stream module. + + + + + +директива proxy_pass_trailers в модуле ngx_http_proxy_module. + + +the "proxy_pass_trailers" directive in the ngx_http_proxy_module. + + + + + +директива ssl_client_certificate теперь поддерживает сертификаты +с дополнительными данными. + + +the "ssl_client_certificate" directive now supports certificates +with auxiliary information. + + + + + +теперь наличие директивы ssl_client_certificate не обязательно +для проверки клиентских SSL-сертификатов. + + +now the "ssl_client_certificate" directive is not required +for client SSL certificates verification. + + + + + + From noreply at nginx.com Wed Oct 2 15:15:03 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Wed, 2 Oct 2024 15:15:03 +0000 (UTC) Subject: [nginx] Annotated tag created: release-1.27.2 Message-ID: <20241002151503.23565477AE@pubserv1.nginx> details: https://github.com/nginx/nginx/releases/tag/release-1.27.2 branches: commit: b06e59c73ca45df937c5d93c0cc473e2bdcf4607 user: Sergey Kandaurov date: Wed Oct 2 18:57:34 2024 +0400 description: release-1.27.2 tag From noreply at nginx.com Wed Oct 2 16:45:17 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Wed, 2 Oct 2024 16:45:17 +0000 (UTC) Subject: [nginx] Annotated tag created: release-1.27.2 Message-ID: <20241002164517.74D88477AF@pubserv1.nginx> details: https://github.com/nginx/nginx/releases/tag/release-1.27.2 branches: commit: e24f7ccc161f1a2a759eb27263ec9af4fc7c8e96 user: Sergey Kandaurov date: Wed Oct 2 19:15:47 2024 +0400 description: release-1.27.2 tag From noreply at nginx.com Tue Oct 8 04:35:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 8 Oct 2024 04:35:02 +0000 (UTC) Subject: [njs] Version bump. Message-ID: <20241008043502.8C0FF4783D@pubserv1.nginx> details: https://github.com/nginx/njs/commit/f2e18cde418f99b0cdb5f8965a8eda8c7020f462 branches: master commit: f2e18cde418f99b0cdb5f8965a8eda8c7020f462 user: Dmitry Volyntsev date: Mon, 7 Oct 2024 18:09:47 -0700 description: Version bump. --- src/njs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/njs.h b/src/njs.h index 8950a0db..3b42ae61 100644 --- a/src/njs.h +++ b/src/njs.h @@ -11,8 +11,8 @@ #include -#define NJS_VERSION "0.8.6" -#define NJS_VERSION_NUMBER 0x000806 +#define NJS_VERSION "0.8.7" +#define NJS_VERSION_NUMBER 0x000807 #include From noreply at nginx.com Tue Oct 8 04:35:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 8 Oct 2024 04:35:02 +0000 (UTC) Subject: [njs] Fixed dead store assignment in TextEncoder.encodeInto(). Message-ID: <20241008043502.A0AB547840@pubserv1.nginx> details: https://github.com/nginx/njs/commit/34e322521508cba30bafc5cc60063a82ca358312 branches: master commit: 34e322521508cba30bafc5cc60063a82ca358312 user: Dmitry Volyntsev date: Tue, 24 Sep 2024 22:07:44 -0700 description: Fixed dead store assignment in TextEncoder.encodeInto(). Found by Clang static analyzer. --- src/njs_encoding.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/njs_encoding.c b/src/njs_encoding.c index 98a73338..f70c084d 100644 --- a/src/njs_encoding.c +++ b/src/njs_encoding.c @@ -204,7 +204,6 @@ njs_text_encoder_encode_into(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, to = njs_typed_array_start(array); to_end = to + array->byte_length; - cp = 0; njs_set_number(&read, 0); njs_set_number(&written, 0); From noreply at nginx.com Tue Oct 8 04:35:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 8 Oct 2024 04:35:02 +0000 (UTC) Subject: [njs] Tests: external code for unit tests is rewritten using public API. Message-ID: <20241008043502.9C6A84783F@pubserv1.nginx> details: https://github.com/nginx/njs/commit/0279dff2fcd3e288b3a0d1698cee2204b9ec6de0 branches: master commit: 0279dff2fcd3e288b3a0d1698cee2204b9ec6de0 user: Dmitry Volyntsev date: Mon, 7 Oct 2024 17:57:08 -0700 description: Tests: external code for unit tests is rewritten using public API. This part was missed in 24b56ef88 (0.8.0). --- src/test/njs_externals_test.c | 127 +++++++++++++++++++++++++----------------- 1 file changed, 75 insertions(+), 52 deletions(-) diff --git a/src/test/njs_externals_test.c b/src/test/njs_externals_test.c index f91c919a..7d2e82af 100644 --- a/src/test/njs_externals_test.c +++ b/src/test/njs_externals_test.c @@ -4,7 +4,10 @@ * Copyright (C) NGINX, Inc. */ -#include +#include +#include +#include +#include #include "njs_externals_test.h" @@ -21,8 +24,8 @@ typedef struct { typedef struct { - njs_value_t name; - njs_value_t value; + njs_str_t name; + njs_opaque_value_t value; } njs_unit_test_prop_t; @@ -59,7 +62,7 @@ lvlhsh_unit_test_key_test(njs_lvlhsh_query_t *lhq, void *data) njs_unit_test_prop_t *prop; prop = data; - njs_string_get(&prop->name, &name); + name = prop->name; if (name.length != lhq->key.length) { return NJS_DECLINED; @@ -96,18 +99,21 @@ static const njs_lvlhsh_proto_t lvlhsh_proto njs_aligned(64) = { static njs_unit_test_prop_t * -lvlhsh_unit_test_alloc(njs_mp_t *pool, const njs_value_t *name, +lvlhsh_unit_test_alloc(njs_mp_t *pool, const njs_str_t *name, const njs_value_t *value) { njs_unit_test_prop_t *prop; - prop = njs_mp_alloc(pool, sizeof(njs_unit_test_prop_t)); + prop = njs_mp_alloc(pool, sizeof(njs_unit_test_prop_t) + name->length); if (prop == NULL) { return NULL; } - prop->name = *name; - prop->value = *value; + prop->name.length = name->length; + prop->name.start = (u_char *) prop + sizeof(njs_unit_test_prop_t); + memcpy(prop->name.start, name->start, name->length); + + njs_value_assign(&prop->value, value); return prop; } @@ -119,7 +125,7 @@ lvlhsh_unit_test_add(njs_mp_t *pool, njs_unit_test_req_t *r, { njs_lvlhsh_query_t lhq; - njs_string_get(&prop->name, &lhq.key); + lhq.key = prop->name; lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length); lhq.replace = 1; @@ -231,7 +237,6 @@ njs_unit_test_r_vars(njs_vm_t *vm, njs_object_prop_t *self, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { njs_int_t ret; - njs_value_t name; njs_lvlhsh_query_t lhq; njs_unit_test_req_t *r; njs_unit_test_prop_t *prop; @@ -264,14 +269,13 @@ njs_unit_test_r_vars(njs_vm_t *vm, njs_object_prop_t *self, if (setval != NULL) { /* Set. */ - njs_vm_value_string_create(vm, &name, lhq.key.start, lhq.key.length); - prop = lvlhsh_unit_test_alloc(vm->mem_pool, &name, setval); + prop = lvlhsh_unit_test_alloc(njs_vm_memory_pool(vm), &lhq.key, setval); if (prop == NULL) { - njs_memory_error(vm); + njs_vm_memory_error(vm); return NJS_ERROR; } - ret = lvlhsh_unit_test_add(vm->mem_pool, r, prop); + ret = lvlhsh_unit_test_add(njs_vm_memory_pool(vm), r, prop); if (ret != NJS_OK) { njs_vm_error(vm, "lvlhsh_unit_test_add() failed"); return NJS_ERROR; @@ -291,12 +295,12 @@ njs_unit_test_r_vars(njs_vm_t *vm, njs_object_prop_t *self, if (ret == NJS_OK) { if (retval == NULL) { - njs_set_invalid(&prop->value); + njs_value_invalid_set(njs_value_arg(&prop->value)); return NJS_OK; } - if (njs_is_valid(&prop->value)) { - *retval = prop->value; + if (njs_value_is_valid(njs_value_arg(&prop->value))) { + njs_value_assign(retval, njs_value_arg(&prop->value)); return NJS_OK; } } @@ -377,7 +381,7 @@ njs_unit_test_r_method(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, r = njs_vm_external(vm, njs_external_r_proto_id, njs_argument(args, 0)); if (r == NULL) { - njs_type_error(vm, "\"this\" is not an external"); + njs_vm_type_error(vm, "\"this\" is not an external"); return NJS_ERROR; } @@ -387,7 +391,7 @@ njs_unit_test_r_method(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, r->uri.length); } - njs_set_undefined(retval); + njs_value_undefined_set(retval); return NJS_OK; } @@ -414,25 +418,27 @@ njs_unit_test_r_subrequest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; - njs_value_t value, *argument, *select; + njs_value_t *argument, *select; njs_function_t *callback; njs_external_ev_t *ev; njs_external_env_t *env; + njs_opaque_value_t value; njs_unit_test_req_t *r; r = njs_vm_external(vm, njs_external_r_proto_id, njs_argument(args, 0)); if (r == NULL) { - njs_type_error(vm, "\"this\" is not an external"); + njs_vm_type_error(vm, "\"this\" is not an external"); return NJS_ERROR; } ev = njs_mp_alloc(njs_vm_memory_pool(vm), sizeof(njs_external_ev_t)); if (ev == NULL) { - njs_memory_error(vm); + njs_vm_memory_error(vm); return NJS_ERROR; } - ret = njs_vm_promise_create(vm, &value, njs_value_arg(&ev->callbacks[0])); + ret = njs_vm_promise_create(vm, njs_value_arg(&value), + njs_value_arg(&ev->callbacks[0])); if (ret != NJS_OK) { return NJS_ERROR; } @@ -449,10 +455,10 @@ njs_unit_test_r_subrequest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, ev->function = callback; ev->data = r; ev->nargs = 2; - njs_value_assign(&ev->args[0], &ev->callbacks[!!njs_bool(select)]); + njs_value_assign(&ev->args[0], &ev->callbacks[!!njs_value_bool(select)]); njs_value_assign(&ev->args[1], argument); - env = vm->external; + env = njs_vm_external_ptr(vm); njs_queue_insert_tail(&env->events, &ev->link); @@ -468,11 +474,11 @@ njs_unit_test_r_retval(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, { njs_external_env_t *env; - env = vm->external; + env = njs_vm_external_ptr(vm); njs_value_assign(&env->retval, njs_arg(args, nargs, 1)); - njs_set_undefined(retval); + njs_value_undefined_set(retval); return NJS_OK; } @@ -497,11 +503,11 @@ njs_unit_test_r_create(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, r = njs_vm_external(vm, njs_external_r_proto_id, njs_argument(args, 0)); if (r == NULL) { - njs_type_error(vm, "\"this\" is not an external"); + njs_vm_type_error(vm, "\"this\" is not an external"); return NJS_ERROR; } - sr = njs_mp_zalloc(vm->mem_pool, sizeof(njs_unit_test_req_t)); + sr = njs_mp_zalloc(njs_vm_memory_pool(vm), sizeof(njs_unit_test_req_t)); if (sr == NULL) { goto memory_error; } @@ -522,7 +528,7 @@ njs_unit_test_r_create(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, memory_error: - njs_memory_error(vm); + njs_vm_memory_error(vm); return NJS_ERROR; } @@ -537,7 +543,7 @@ njs_unit_test_r_bind(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, r = njs_vm_external(vm, njs_external_r_proto_id, njs_argument(args, 0)); if (r == NULL) { - njs_type_error(vm, "\"this\" is not an external"); + njs_vm_type_error(vm, "\"this\" is not an external"); return NJS_ERROR; } @@ -559,7 +565,7 @@ njs_unit_test_null_get(njs_vm_t *vm, njs_value_t *args, this = njs_argument(args, 0); if (!njs_value_is_external(this, njs_external_null_proto_id)) { - njs_type_error(vm, "\"this\" is not a null external"); + njs_vm_type_error(vm, "\"this\" is not a null external"); return NJS_ERROR; } @@ -586,16 +592,16 @@ njs_unit_test_null_set(njs_vm_t *vm, njs_value_t *args, this = njs_argument(args, 0); if (!njs_value_is_external(this, njs_external_null_proto_id)) { - njs_type_error(vm, "\"this\" is not a null external"); + njs_vm_type_error(vm, "\"this\" is not a null external"); return NJS_ERROR; } d = njs_value_external(this); if (d == NULL) { - d = njs_mp_alloc(vm->mem_pool, sizeof(double)); + d = njs_mp_alloc(njs_vm_memory_pool(vm), sizeof(double)); if (d == NULL) { - njs_memory_error(vm); + njs_vm_memory_error(vm); return NJS_ERROR; } } @@ -616,9 +622,9 @@ njs_unit_test_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, { njs_unit_test_req_t *sr; - sr = njs_mp_zalloc(vm->mem_pool, sizeof(njs_unit_test_req_t)); + sr = njs_mp_zalloc(njs_vm_memory_pool(vm), sizeof(njs_unit_test_req_t)); if (sr == NULL) { - njs_memory_error(vm); + njs_vm_memory_error(vm); return NJS_ERROR; } @@ -1017,9 +1023,15 @@ static njs_external_t njs_unit_test_proto_props[] = { typedef struct { - njs_str_t name; - njs_unit_test_req_t request; - njs_unit_test_prop_t props[2]; + njs_str_t name; + njs_str_t value; +} njs_unit_test_prop_init_t; + + +typedef struct { + njs_str_t name; + njs_unit_test_req_t request; + njs_unit_test_prop_init_t props[2]; } njs_unit_test_req_init_t; @@ -1032,8 +1044,8 @@ static njs_unit_test_req_init_t njs_test_requests[] = { .d = 13, }, { - { njs_string("r"), njs_string("rval") }, - { njs_string("r2"), njs_string("r2val") }, + { njs_str("r"), njs_str("rval") }, + { njs_str("r2"), njs_str("r2val") }, } }, @@ -1044,8 +1056,8 @@ static njs_unit_test_req_init_t njs_test_requests[] = { .d = 1024, }, { - { njs_string("p"), njs_string("pval") }, - { njs_string("p2"), njs_string("p2val") }, + { njs_str("p"), njs_str("pval") }, + { njs_str("p2"), njs_str("p2val") }, } }, @@ -1056,8 +1068,8 @@ static njs_unit_test_req_init_t njs_test_requests[] = { .d = 1025, }, { - { njs_string("q"), njs_string("qval") }, - { njs_string("q2"), njs_string("q2val") }, + { njs_str("q"), njs_str("qval") }, + { njs_str("q2"), njs_str("q2val") }, } }, @@ -1068,8 +1080,8 @@ static njs_unit_test_req_init_t njs_test_requests[] = { .d = 1026, }, { - { njs_string("k"), njs_string("kval") }, - { njs_string("k2"), njs_string("k2val") }, + { njs_str("k"), njs_str("kval") }, + { njs_str("k2"), njs_str("k2val") }, } }, }; @@ -1081,10 +1093,12 @@ njs_externals_init_internal(njs_vm_t *vm, njs_unit_test_req_init_t *init, { njs_int_t ret; njs_uint_t i, j; + njs_opaque_value_t value; njs_unit_test_req_t *requests; njs_unit_test_prop_t *prop; - requests = njs_mp_zalloc(vm->mem_pool, n * sizeof(njs_unit_test_req_t)); + requests = njs_mp_zalloc(njs_vm_memory_pool(vm), + n * sizeof(njs_unit_test_req_t)); if (njs_slow_path(requests == NULL)) { return NJS_ERROR; } @@ -1108,15 +1122,24 @@ njs_externals_init_internal(njs_vm_t *vm, njs_unit_test_req_init_t *init, } for (j = 0; j < njs_nitems(init[i].props); j++) { - prop = lvlhsh_unit_test_alloc(vm->mem_pool, &init[i].props[j].name, - &init[i].props[j].value); + ret = njs_vm_value_string_create(vm, njs_value_arg(&value), + init[i].props[j].value.start, + init[i].props[j].value.length); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + + prop = lvlhsh_unit_test_alloc(njs_vm_memory_pool(vm), + &init[i].props[j].name, + njs_value_arg(&value)); if (njs_slow_path(prop == NULL)) { njs_printf("lvlhsh_unit_test_alloc() failed\n"); return NJS_ERROR; } - ret = lvlhsh_unit_test_add(vm->mem_pool, &requests[i], prop); + ret = lvlhsh_unit_test_add(njs_vm_memory_pool(vm), &requests[i], + prop); if (njs_slow_path(ret != NJS_OK)) { njs_printf("lvlhsh_unit_test_add() failed\n"); return NJS_ERROR; From noreply at nginx.com Tue Oct 8 04:35:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 8 Oct 2024 04:35:02 +0000 (UTC) Subject: [njs] Style. Message-ID: <20241008043502.A4B6647841@pubserv1.nginx> details: https://github.com/nginx/njs/commit/4bf2d068fee6ebf09b33954f00ae94ec8b741f3e branches: master commit: 4bf2d068fee6ebf09b33954f00ae94ec8b741f3e user: Dmitry Volyntsev date: Mon, 7 Oct 2024 18:12:12 -0700 description: Style. --- src/njs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/njs.h b/src/njs.h index 3b42ae61..466932d8 100644 --- a/src/njs.h +++ b/src/njs.h @@ -295,7 +295,7 @@ NJS_EXPORT void njs_vm_destroy(njs_vm_t *vm); NJS_EXPORT njs_int_t njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end); NJS_EXPORT void njs_vm_set_module_loader(njs_vm_t *vm, - njs_module_loader_t module_loader, void *opaque); + njs_module_loader_t module_loader, void *opaque); NJS_EXPORT njs_mod_t *njs_vm_add_module(njs_vm_t *vm, njs_str_t *name, njs_value_t *value); NJS_EXPORT njs_mod_t *njs_vm_compile_module(njs_vm_t *vm, njs_str_t *name, @@ -314,7 +314,7 @@ NJS_EXPORT njs_int_t njs_vm_execute_pending_job(njs_vm_t *vm); NJS_EXPORT njs_int_t njs_vm_pending(njs_vm_t *vm); NJS_EXPORT void njs_vm_set_rejection_tracker(njs_vm_t *vm, - njs_rejection_tracker_t rejection_tracker, void *opaque); + njs_rejection_tracker_t rejection_tracker, void *opaque); /* * Runs the specified function with provided arguments. From noreply at nginx.com Tue Oct 8 04:35:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 8 Oct 2024 04:35:02 +0000 (UTC) Subject: [njs] Removed deprecated $262.byteString(). Message-ID: <20241008043502.977784783E@pubserv1.nginx> details: https://github.com/nginx/njs/commit/efa5e7d653548505d05d66eaed609db9042b834e branches: master commit: efa5e7d653548505d05d66eaed609db9042b834e user: Dmitry Volyntsev date: Mon, 7 Oct 2024 17:50:19 -0700 description: Removed deprecated $262.byteString(). --- src/test/njs_externals_test.c | 173 ------------------------------------------ src/test/njs_unit_test.c | 55 +------------- 2 files changed, 1 insertion(+), 227 deletions(-) diff --git a/src/test/njs_externals_test.c b/src/test/njs_externals_test.c index 7dcad90b..f91c919a 100644 --- a/src/test/njs_externals_test.c +++ b/src/test/njs_externals_test.c @@ -650,168 +650,6 @@ njs_unit_test_error_message(njs_vm_t *vm, njs_object_prop_t *prop, } -static njs_int_t -njs_262_bytes_from_array_like(njs_vm_t *vm, njs_value_t *value, - njs_value_t *retval) -{ - u_char *p; - int64_t length; - uint32_t u32; - njs_int_t ret; - njs_array_t *array; - njs_value_t *octet, index, prop; - njs_array_buffer_t *buffer; - - array = NULL; - buffer = NULL; - - switch (value->type) { - case NJS_ARRAY: - array = njs_array(value); - length = array->length; - break; - - case NJS_ARRAY_BUFFER: - case NJS_TYPED_ARRAY: - - if (njs_is_typed_array(value)) { - buffer = njs_typed_array(value)->buffer; - - } else { - buffer = njs_array_buffer(value); - } - - length = buffer->size; - break; - - default: - ret = njs_object_length(vm, value, &length); - if (njs_slow_path(ret == NJS_ERROR)) { - return ret; - } - } - - p = njs_string_alloc(vm, retval, length, 0); - if (njs_slow_path(p == NULL)) { - return NJS_ERROR; - } - - if (array != NULL) { - octet = array->start; - - while (length != 0) { - ret = njs_value_to_uint32(vm, octet, &u32); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - - *p++ = (u_char) u32; - octet++; - length--; - } - - } else if (buffer != NULL) { - memcpy(p, buffer->u.u8, length); - - } else { - p += length - 1; - - while (length != 0) { - njs_set_number(&index, length - 1); - - ret = njs_value_property(vm, value, &index, &prop); - if (njs_slow_path(ret == NJS_ERROR)) { - return ret; - } - - ret = njs_value_to_uint32(vm, &prop, &u32); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - - *p-- = (u_char) u32; - length--; - } - } - - return NJS_OK; -} - - -static njs_int_t -njs_262_bytes_from_string(njs_vm_t *vm, const njs_value_t *string, - const njs_value_t *encoding, njs_value_t *retval) -{ - njs_str_t enc, str; - - if (!njs_is_string(encoding)) { - njs_type_error(vm, "\"encoding\" must be a string"); - return NJS_ERROR; - } - - njs_string_get(encoding, &enc); - njs_string_get(string, &str); - - if (enc.length == 3 && memcmp(enc.start, "hex", 3) == 0) { - return njs_string_decode_hex(vm, retval, &str); - - } else if (enc.length == 6 && memcmp(enc.start, "base64", 6) == 0) { - return njs_string_decode_base64(vm, retval, &str); - - } else if (enc.length == 9 && memcmp(enc.start, "base64url", 9) == 0) { - return njs_string_decode_base64url(vm, retval, &str); - } - - njs_type_error(vm, "Unknown encoding: \"%V\"", &enc); - - return NJS_ERROR; -} - - -/* - * $262.byteString(array-like). - * Converts an array-like object containing octets into a byte string. - * - * $262.byteString(string[, encoding]). - * Converts a string using provided encoding: hex, base64, base64url to - * a byte string. - * - * Note: the function produces a byte string, and byte strings are deprecated. - * The function is provided for testing of existing code which works with - * byte strings. When code working with byte strings is removed - * the function will be removed as well. - */ - -static njs_int_t -njs_262_byte_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, - njs_index_t unused, njs_value_t *retval) -{ - njs_value_t *value; - - value = njs_arg(args, nargs, 1); - - if (njs_is_string(value)) { - return njs_262_bytes_from_string(vm, value, njs_arg(args, nargs, 2), - retval); - - } else if (njs_is_object(value)) { - - if (njs_is_object_string(value)) { - value = njs_object_value(value); - return njs_262_bytes_from_string(vm, value, - njs_arg(args, nargs, 2), - retval); - } - - return njs_262_bytes_from_array_like(vm, value, retval); - } - - njs_type_error(vm, "value must be a string or array-like object"); - - return NJS_ERROR; -} - - static njs_external_t njs_unit_test_262_external[] = { { @@ -833,17 +671,6 @@ static njs_external_t njs_unit_test_262_external[] = { } }, - { - .flags = NJS_EXTERN_METHOD, - .name.string = njs_str("byteString"), - .writable = 1, - .configurable = 1, - .enumerable = 1, - .u.method = { - .native = njs_262_byte_string, - } - }, - }; diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index 10ee6c1e..c52753a7 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -10028,9 +10028,6 @@ static njs_unit_test_t njs_test[] = { njs_str("decodeURI('%D0%B0%D0%B1%D0%B2').length"), njs_str("3")}, - { njs_str("decodeURI($262.byteString([0x80,0x80]))"), - njs_str("URIError: malformed URI")}, - { njs_str("[" " '%'," " '%0'," @@ -10074,11 +10071,8 @@ static njs_unit_test_t njs_test[] = " String.fromCodePoint(0x100)," " String.fromCodePoint(0x00, 0x100)," " String.fromCodePoint(0x00, 0x01, 0x100)," - " $262.byteString([0x80])," - " $262.byteString([0x60, 0x80])," - " $262.byteString([0x60, 0x60, 0x80])," "].map(v => { try { return btoa(v); } catch (e) { return '#'} })"), - njs_str("dW5kZWZpbmVk,,AA==,AAE=,AAEC,AP7/,#,#,#,#,#,#")}, + njs_str("dW5kZWZpbmVk,,AA==,AAE=,AAEC,AP7/,#,#,#")}, /* atob() */ @@ -19359,17 +19353,6 @@ static njs_unit_test_t njs_test[] = { njs_str("var en = new TextEncoder(); var res = en.encode('α1α'); res[2]"), njs_str("49") }, - { njs_str("var en = new TextEncoder(); en.encode($262.byteString([0xCE]))"), - njs_str("239,191,189") }, - - { njs_str("var en = new TextEncoder();" - "en.encode($262.byteString([0xCE, 0xB1, 0xCE]))"), - njs_str("206,177,239,191,189") }, - - { njs_str("var en = new TextEncoder();" - "en.encode($262.byteString([0xCE, 0xCE, 0xB1]))"), - njs_str("239,191,189,206,177") }, - { njs_str("var en = new TextEncoder(); en.encoding"), njs_str("utf-8") }, @@ -19391,33 +19374,6 @@ static njs_unit_test_t njs_test[] = "en.encodeInto('ααααα', utf8.subarray(2)); utf8[0]"), njs_str("0") }, - { njs_str("var str = $262.byteString([0xCE]);" - "var en = new TextEncoder();" - "var utf8 = new Uint8Array(3);" - "var res = en.encodeInto(str, utf8); " - "[njs.dump(res), utf8]"), - njs_str("{read:1,written:3},239,191,189") }, - - { njs_str("var str = $262.byteString([0xCE]);" - "var en = new TextEncoder();" - "var utf8 = new Uint8Array(5);" - "en.encodeInto(str, utf8); utf8"), - njs_str("239,191,189,0,0") }, - - { njs_str("var str = $262.byteString([0xCE, 0xB1, 0xCE]);" - "var en = new TextEncoder();" - "var utf8 = new Uint8Array(5);" - "var res = en.encodeInto(str, utf8);" - "[njs.dump(res), utf8]"), - njs_str("{read:2,written:5},206,177,239,191,189") }, - - { njs_str("var str = $262.byteString([0xCE, 0xCE, 0xB1]);" - "var en = new TextEncoder();" - "var utf8 = new Uint8Array(5);" - "var res = en.encodeInto(str, utf8);" - "[njs.dump(res), utf8]"), - njs_str("{read:2,written:5},239,191,189,206,177") }, - { njs_str("TextEncoder.prototype.encodeInto.apply({}, [])"), njs_str("TypeError: \"this\" is not a TextEncoder") }, @@ -20901,11 +20857,6 @@ static njs_unit_test_t njs_querystring_module_test[] = "out.join('; ')"), njs_str("baz; fuz; muz; tax") }, - { njs_str("var qs = require('querystring'); " - "qs.stringify({a: 'b'}, null, null, " - " {encodeURIComponent: () => $262.byteString([0x9d])})"), - njs_str("InternalError: invalid UTF-8 string") }, - { njs_str("var qs = require('querystring');" "qs.stringify({'baz': 'fuz', 'muz': 'tax'}, null, null, {encodeURIComponent: 123});" "out.join('; ')"), @@ -20955,10 +20906,6 @@ static njs_unit_test_t njs_querystring_module_test[] = "qs.stringify(123)"), njs_str("") }, - { njs_str("var qs = require('querystring');" - "qs.stringify({X: $262.byteString(Array(4).fill(0x9d))})"), - njs_str("X=%9D%9D%9D%9D") }, - { njs_str("var qs = require('querystring');" "qs.stringify({X:{toString(){return 3}}})"), njs_str("X=") }, From noreply at nginx.com Tue Oct 8 04:35:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 8 Oct 2024 04:35:02 +0000 (UTC) Subject: [njs] Benchmark: code is rewritten using public API. Message-ID: <20241008043502.AFC9747843@pubserv1.nginx> details: https://github.com/nginx/njs/commit/30a965832bb68b232fa2007919267f8827661800 branches: master commit: 30a965832bb68b232fa2007919267f8827661800 user: Dmitry Volyntsev date: Mon, 7 Oct 2024 19:08:53 -0700 description: Benchmark: code is rewritten using public API. --- src/test/njs_benchmark.c | 99 +++++++++++++++++++++++++++++------------------- 1 file changed, 61 insertions(+), 38 deletions(-) diff --git a/src/test/njs_benchmark.c b/src/test/njs_benchmark.c index 71de6b57..6ca83e82 100644 --- a/src/test/njs_benchmark.c +++ b/src/test/njs_benchmark.c @@ -4,7 +4,8 @@ * Copyright (C) NGINX, Inc. */ -#include +#include +#include #include "njs_externals_test.h" @@ -98,19 +99,20 @@ static njs_int_t njs_benchmark_test(njs_vm_t *parent, njs_opts_t *opts, njs_value_t *report, njs_benchmark_test_t *test) { - u_char *start; - njs_vm_t *vm, *nvm; - uint64_t ns; - njs_int_t ret; - njs_str_t s, *expected; - njs_uint_t i, n; - njs_bool_t success; - njs_value_t *result, retval, name, usec, times; - njs_vm_opt_t options; - - static const njs_value_t name_key = njs_string("name"); - static const njs_value_t usec_key = njs_string("usec"); - static const njs_value_t times_key = njs_string("times"); + u_char *start; + njs_vm_t *vm, *nvm; + uint64_t ns; + njs_int_t ret; + njs_str_t s, *expected; + njs_uint_t i, n; + njs_bool_t success; + njs_value_t *result; + njs_vm_opt_t options; + njs_opaque_value_t retval, name, usec, times; + + static const njs_str_t name_key = njs_str("name"); + static const njs_str_t usec_key = njs_str("usec"); + static const njs_str_t times_key = njs_str("times"); njs_vm_opt_init(&options); @@ -148,9 +150,9 @@ njs_benchmark_test(njs_vm_t *parent, njs_opts_t *opts, njs_value_t *report, goto done; } - (void) njs_vm_start(nvm, &retval); + (void) njs_vm_start(nvm, njs_value_arg(&retval)); - if (njs_vm_value_string(nvm, &s, &retval) != NJS_OK) { + if (njs_vm_value_string(nvm, &s, njs_value_arg(&retval)) != NJS_OK) { njs_printf("njs_vm_value_string() failed\n"); goto done; } @@ -188,23 +190,41 @@ njs_benchmark_test(njs_vm_t *parent, njs_opts_t *opts, njs_value_t *report, goto done; } - ret = njs_vm_value_string_create(parent, &name, (u_char *) test->name, + ret = njs_vm_value_string_create(parent, njs_value_arg(&name), + (u_char *) test->name, njs_strlen(test->name)); if (ret != NJS_OK) { njs_printf("njs_vm_value_string_create() failed\n"); goto done; } - njs_value_number_set(&usec, 1000 * ns); - njs_value_number_set(×, n); + njs_value_number_set(njs_value_arg(&usec), 1000 * ns); + njs_value_number_set(njs_value_arg(×), n); - ret = njs_vm_object_alloc(parent, result, &name_key, &name, - &usec_key, &usec, ×_key, ×, NULL); + ret = njs_vm_object_alloc(parent, result, NULL); if (ret != NJS_OK) { njs_printf("njs_vm_object_alloc() failed\n"); goto done; } + ret = njs_vm_object_prop_set(parent, result, &name_key, &name); + if (ret != NJS_OK) { + njs_printf("njs_vm_object_prop_set() failed\n"); + goto done; + } + + ret = njs_vm_object_prop_set(parent, result, &usec_key, &usec); + if (ret != NJS_OK) { + njs_printf("njs_vm_object_prop_set() failed\n"); + goto done; + } + + ret = njs_vm_object_prop_set(parent, result, ×_key, ×); + if (ret != NJS_OK) { + njs_printf("njs_vm_object_prop_set() failed\n"); + goto done; + } + ret = NJS_OK; done: @@ -483,8 +503,8 @@ main(int argc, char **argv) njs_str_t out; njs_uint_t i; njs_opts_t opts; - njs_value_t args[2], report, retval; njs_vm_opt_t options; + njs_opaque_value_t args[2], report, retval; njs_benchmark_test_t *test; static const char help[] = @@ -563,9 +583,9 @@ main(int argc, char **argv) goto done; } - njs_vm_start(vm, &retval); + njs_vm_start(vm, njs_value_arg(&retval)); - ret = njs_vm_array_alloc(vm, &report, 8); + ret = njs_vm_array_alloc(vm, njs_value_arg(&report), 8); if (ret != NJS_OK) { njs_printf("njs_vm_array_alloc() failed\n"); goto done; @@ -581,7 +601,7 @@ main(int argc, char **argv) if (strncmp(test->name, opts.prefix, njs_min(strlen(test->name), strlen(opts.prefix))) == 0) { - ret = njs_benchmark_test(vm, &opts, &report, test); + ret = njs_benchmark_test(vm, &opts, njs_value_arg(&report), test); if (ret != NJS_OK) { goto done; @@ -590,19 +610,20 @@ main(int argc, char **argv) } if (opts.previous) { - ret = njs_vm_value_string_create(vm, &args[0], (u_char *) opts.previous, + ret = njs_vm_value_string_create(vm, njs_value_arg(&args[0]), + (u_char *) opts.previous, njs_strlen(opts.previous)); if (ret != NJS_OK) { njs_printf("njs_vm_value_string_create() failed\n"); goto done; } - args[1] = report; + njs_value_assign(&args[1], &report); njs_vm_invoke(vm, njs_vm_function(vm, &compare), njs_value_arg(&args), - 2, &retval); + 2, njs_value_arg(&retval)); - ret = njs_vm_value_dump(vm, &out, &retval, 1, 1); + ret = njs_vm_value_dump(vm, &out, njs_value_arg(&retval), 1, 1); if (ret != NJS_OK) { njs_printf("njs_vm_value_dump() failed\n"); goto done; @@ -614,13 +635,14 @@ main(int argc, char **argv) } if (opts.dump_report) { - ret = njs_vm_json_stringify(vm, &report, 1, &retval); + ret = njs_vm_json_stringify(vm, njs_value_arg(&report), 1, + njs_value_arg(&retval)); if (ret != NJS_OK) { njs_printf("njs_vm_json_stringify() failed\n"); goto done; } - ret = njs_vm_value_dump(vm, &out, &retval, 1, 1); + ret = njs_vm_value_dump(vm, &out, njs_value_arg(&retval), 1, 1); if (ret != NJS_OK) { njs_printf("njs_vm_value_dump() failed\n"); goto done; @@ -690,10 +712,10 @@ static njs_int_t njs_benchmark_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { - int64_t i, n; - njs_chb_t chain; - njs_str_t s, mode; - njs_value_t value; + int64_t i, n; + njs_chb_t chain; + njs_str_t s, mode; + njs_opaque_value_t value; njs_value_string_get(njs_arg(args, nargs, 1), &mode); @@ -706,7 +728,8 @@ njs_benchmark_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, if (memcmp(mode.start, "create", 6) == 0) { for (i = 0; i < n; i++) { - njs_string_create(vm, &value, s.start, s.length); + njs_vm_value_string_create(vm, njs_value_arg(&value), s.start, + s.length); } } else if (memcmp(mode.start, "chb", 3) == 0) { @@ -721,13 +744,13 @@ njs_benchmark_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_chb_append(&chain, s.start, s.length); for (i = 0; i < n; i++) { - njs_string_create_chb(vm, &value, &chain); + njs_vm_value_string_create_chb(vm, njs_value_arg(&value), &chain); } njs_chb_destroy(&chain); } else { - njs_type_error(vm, "unknown mode \"%V\"", &mode); + njs_vm_type_error(vm, "unknown mode \"%V\"", &mode); return NJS_ERROR; } From noreply at nginx.com Tue Oct 8 04:35:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 8 Oct 2024 04:35:02 +0000 (UTC) Subject: [njs] Removed deprecated njs_string_set(). Message-ID: <20241008043502.AAD5447842@pubserv1.nginx> details: https://github.com/nginx/njs/commit/cf889c11f2fca991452dbdc7fe40cc4e05b708d4 branches: master commit: cf889c11f2fca991452dbdc7fe40cc4e05b708d4 user: Dmitry Volyntsev date: Mon, 7 Oct 2024 18:51:03 -0700 description: Removed deprecated njs_string_set(). --- src/njs_string.c | 52 ------------------------------------------------ src/njs_string.h | 3 --- src/test/njs_benchmark.c | 27 +------------------------ 3 files changed, 1 insertion(+), 81 deletions(-) diff --git a/src/njs_string.c b/src/njs_string.c index ea22755b..9405a8db 100644 --- a/src/njs_string.c +++ b/src/njs_string.c @@ -74,58 +74,6 @@ static njs_int_t njs_string_match_multiple(njs_vm_t *vm, njs_value_t *args, #define njs_base64_decoded_length(len, pad) (((len / 4) * 3) - pad) -njs_int_t -njs_string_set(njs_vm_t *vm, njs_value_t *value, const u_char *start, - uint32_t size) -{ - u_char *dst; - const u_char *src; - njs_string_t *string; - - value->type = NJS_STRING; - njs_string_truth(value, size); - - if (size <= NJS_STRING_SHORT) { - value->short_string.size = size; - value->short_string.length = 0; - - dst = value->short_string.start; - src = start; - - while (size != 0) { - /* The maximum size is just 14 bytes. */ - njs_pragma_loop_disable_vectorization; - - *dst++ = *src++; - size--; - } - - } else { - /* - * Setting UTF-8 length is not required here, it just allows - * to store the constant in whole byte instead of bit twiddling. - */ - value->short_string.size = NJS_STRING_LONG; - value->short_string.length = 0; - value->long_string.external = 0xff; - value->long_string.size = size; - - string = njs_mp_alloc(vm->mem_pool, sizeof(njs_string_t)); - if (njs_slow_path(string == NULL)) { - njs_memory_error(vm); - return NJS_ERROR; - } - - value->long_string.data = string; - - string->start = (u_char *) start; - string->length = 0; - } - - return NJS_OK; -} - - njs_int_t njs_string_create(njs_vm_t *vm, njs_value_t *value, const u_char *src, size_t size) diff --git a/src/njs_string.h b/src/njs_string.h index 9b478875..686a5a74 100644 --- a/src/njs_string.h +++ b/src/njs_string.h @@ -102,9 +102,6 @@ typedef enum { } njs_trim_t; -/* njs_string_set() Is deprecated. */ -njs_int_t njs_string_set(njs_vm_t *vm, njs_value_t *value, const u_char *start, - uint32_t size); u_char *njs_string_alloc(njs_vm_t *vm, njs_value_t *value, uint64_t size, uint64_t length); njs_int_t njs_string_new(njs_vm_t *vm, njs_value_t *value, const u_char *start, diff --git a/src/test/njs_benchmark.c b/src/test/njs_benchmark.c index f957dc56..71de6b57 100644 --- a/src/test/njs_benchmark.c +++ b/src/test/njs_benchmark.c @@ -265,26 +265,6 @@ static njs_benchmark_test_t njs_test[] = njs_str("4"), 100000 }, - { "string set 'abcdefABCDEF'", - njs_str("benchmark.string('set', 'abcdef', 1000000)"), - njs_str("undefined"), - 1 }, - - { "string set 'АБВГДЕ'", - njs_str("benchmark.string('set', 'АБВГДЕ', 1000000)"), - njs_str("undefined"), - 1 }, - - { "string set 'x'.repeat(24)", - njs_str("benchmark.string('set', 'x'.repeat(32), 1000000)"), - njs_str("undefined"), - 1 }, - - { "string set 'Д'.repeat(12)", - njs_str("benchmark.string('set', 'А'.repeat(16), 1000000)"), - njs_str("undefined"), - 1 }, - { "string create 'abcdefABCDEF'", njs_str("benchmark.string('create', 'abcdef', 1000000)"), njs_str("undefined"), @@ -723,12 +703,7 @@ njs_benchmark_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, return NJS_ERROR; } - if (memcmp(mode.start, "set", 3) == 0) { - for (i = 0; i < n; i++) { - njs_string_set(vm, &value, s.start, s.length); - } - - } else if (memcmp(mode.start, "create", 6) == 0) { + if (memcmp(mode.start, "create", 6) == 0) { for (i = 0; i < n; i++) { njs_string_create(vm, &value, s.start, s.length); From noreply at nginx.com Tue Oct 8 14:30:03 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 8 Oct 2024 14:30:03 +0000 (UTC) Subject: [nginx] Version bump. Message-ID: <20241008143003.A754A47845@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/144778aee6e377c9bcd84368848b2e27871d570f branches: master commit: 144778aee6e377c9bcd84368848b2e27871d570f user: Sergey Kandaurov date: Mon, 7 Oct 2024 19:10:39 +0400 description: Version bump. --- src/core/nginx.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/nginx.h b/src/core/nginx.h index 140a29408..fb6fa4737 100644 --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -9,8 +9,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1027002 -#define NGINX_VERSION "1.27.2" +#define nginx_version 1027003 +#define NGINX_VERSION "1.27.3" #define NGINX_VER "nginx/" NGINX_VERSION #ifdef NGX_BUILD From noreply at nginx.com Tue Oct 8 15:56:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 8 Oct 2024 15:56:02 +0000 (UTC) Subject: [nginx] QUIC: prevent deleted stream frame retransmissions. Message-ID: <20241008155602.A514547846@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/3f6d94d8881ca6755b1baa4dd6248a7b7ed15735 branches: master commit: 3f6d94d8881ca6755b1baa4dd6248a7b7ed15735 user: nandsky date: Mon, 30 Sep 2024 20:51:17 +0800 description: QUIC: prevent deleted stream frame retransmissions. Since a2a513b93cae, stream frames no longer need to be retransmitted after it was deleted. The frames which were retransmitted before, could be stream data frames sent prior to a RESET_STREAM. Such retransmissions are explicitly prohibited by RFC 9000, Section 19.4. --- src/event/quic/ngx_event_quic_ack.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/event/quic/ngx_event_quic_ack.c b/src/event/quic/ngx_event_quic_ack.c index c7ffd44dd..c953b8042 100644 --- a/src/event/quic/ngx_event_quic_ack.c +++ b/src/event/quic/ngx_event_quic_ack.c @@ -631,13 +631,12 @@ ngx_quic_resend_frames(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx) case NGX_QUIC_FT_STREAM: qs = ngx_quic_find_stream(&qc->streams.tree, f->u.stream.stream_id); - if (qs) { - if (qs->send_state == NGX_QUIC_STREAM_SEND_RESET_SENT - || qs->send_state == NGX_QUIC_STREAM_SEND_RESET_RECVD) - { - ngx_quic_free_frame(c, f); - break; - } + if (qs == NULL + || qs->send_state == NGX_QUIC_STREAM_SEND_RESET_SENT + || qs->send_state == NGX_QUIC_STREAM_SEND_RESET_RECVD) + { + ngx_quic_free_frame(c, f); + break; } /* fall through */ From noreply at nginx.com Tue Oct 8 23:04:01 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 8 Oct 2024 23:04:01 +0000 (UTC) Subject: [njs] Optimized qjs_to_bytes(). Message-ID: <20241008230401.F389C47855@pubserv1.nginx> details: https://github.com/nginx/njs/commit/39a2d4bf212346d1487e4d27383453cafefa17ea branches: master commit: 39a2d4bf212346d1487e4d27383453cafefa17ea user: Dmitry Volyntsev date: Mon, 7 Oct 2024 22:46:40 -0700 description: Optimized qjs_to_bytes(). Doing JS_IsString() check first before a heavy-weight call to JS_GetTypedArrayBuffer() which throws an exception when argument is not a typed array. --- src/qjs.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/qjs.c b/src/qjs.c index e7653569..3d378fcc 100644 --- a/src/qjs.c +++ b/src/qjs.c @@ -175,6 +175,10 @@ qjs_to_bytes(JSContext *ctx, qjs_bytes_t *bytes, JSValueConst value) size_t byte_offset, byte_length; JSValue val; + if (JS_IsString(value)) { + goto string; + } + val = JS_GetTypedArrayBuffer(ctx, value, &byte_offset, &byte_length, NULL); if (!JS_IsException(val)) { bytes->start = JS_GetArrayBuffer(ctx, &bytes->length, val); @@ -195,8 +199,6 @@ qjs_to_bytes(JSContext *ctx, qjs_bytes_t *bytes, JSValueConst value) return 0; } - bytes->tag = JS_TAG_STRING; - if (!JS_IsString(value)) { val = JS_ToString(ctx, value); @@ -209,6 +211,9 @@ qjs_to_bytes(JSContext *ctx, qjs_bytes_t *bytes, JSValueConst value) } } +string: + + bytes->tag = JS_TAG_STRING; bytes->start = (u_char *) JS_ToCStringLen(ctx, &bytes->length, value); return (bytes->start != NULL) ? 0 : -1; From noreply at nginx.com Tue Oct 8 23:04:01 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 8 Oct 2024 23:04:01 +0000 (UTC) Subject: [njs] Optimized ngx_qjs_string(). Message-ID: <20241008230401.F059047854@pubserv1.nginx> details: https://github.com/nginx/njs/commit/3a4349e6b2b001001c64dd48f7863c5820cdf3e6 branches: master commit: 3a4349e6b2b001001c64dd48f7863c5820cdf3e6 user: Dmitry Volyntsev date: Mon, 7 Oct 2024 22:41:24 -0700 description: Optimized ngx_qjs_string(). Doing JS_IsString() check first before a heavy-weight call to JS_GetTypedArrayBuffer() which throws an exception when argument is not a typed array. --- nginx/ngx_js.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/nginx/ngx_js.c b/nginx/ngx_js.c index 5fe3dc84..98e48dc2 100644 --- a/nginx/ngx_js.c +++ b/nginx/ngx_js.c @@ -1472,6 +1472,10 @@ ngx_qjs_string(ngx_engine_t *e, JSValueConst val, ngx_str_t *dst) cx = e->u.qjs.ctx; + if (JS_IsString(val)) { + goto string; + } + buffer = JS_GetTypedArrayBuffer(cx, val, &byte_offset, &byte_length, NULL); if (!JS_IsException(buffer)) { start = JS_GetArrayBuffer(cx, &dst->len, buffer); @@ -1492,6 +1496,8 @@ ngx_qjs_string(ngx_engine_t *e, JSValueConst val, ngx_str_t *dst) } } +string: + str = JS_ToCString(cx, val); if (str == NULL) { return NGX_ERROR; From noreply at nginx.com Thu Oct 10 16:44:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Thu, 10 Oct 2024 16:44:02 +0000 (UTC) Subject: [njs] Fixed heap-buffer-overflow in Buffer.prototype.indexOf(). Message-ID: <20241010164402.E17FF4786A@pubserv1.nginx> details: https://github.com/nginx/njs/commit/6c8084b666cfe5db5d9401e7dff7981b5b2eb100 branches: master commit: 6c8084b666cfe5db5d9401e7dff7981b5b2eb100 user: Dmitry Volyntsev date: Wed, 9 Oct 2024 17:32:11 -0700 description: Fixed heap-buffer-overflow in Buffer.prototype.indexOf(). Previously, when `from` argument was provided heap-buffer-overflow might happen due to lack of boundary check. `to = njs_min(to, length)` statement was also removed because it has no effect, `to` is equal to `length` here. The issue was introduced in 5d15a8d6 (0.8.5). This closes #794 issue on Github. --- src/njs_buffer.c | 5 ++++- src/qjs_buffer.c | 7 ++++++- test/buffer.t.js | 1 + 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/njs_buffer.c b/src/njs_buffer.c index 07054bf0..0bd5b896 100644 --- a/src/njs_buffer.c +++ b/src/njs_buffer.c @@ -2228,7 +2228,10 @@ encoding: } else { to -= str.length - 1; - to = njs_min(to, length); + + if (from > to) { + goto done; + } } for (i = from; i != to; i += increment) { diff --git a/src/qjs_buffer.c b/src/qjs_buffer.c index 2487c633..5def5e63 100644 --- a/src/qjs_buffer.c +++ b/src/qjs_buffer.c @@ -1098,7 +1098,10 @@ encoding: } else { to -= str.length - 1; - to = njs_min(to, length); + + if (from > to) { + goto done; + } } for (i = from; i != to; i += increment) { @@ -1108,6 +1111,8 @@ encoding: } } +done: + JS_FreeValue(ctx, buffer); return JS_NewInt32(ctx, -1); } diff --git a/test/buffer.t.js b/test/buffer.t.js index 55227b3a..f47c62f7 100644 --- a/test/buffer.t.js +++ b/test/buffer.t.js @@ -473,6 +473,7 @@ let indexOf_tsuite = { { buf: Buffer.from('abcdef'), value: 'abc', offset: 1, expected: -1 }, { buf: Buffer.from('abcdef'), value: 'def', offset: 1, expected: 3 }, { buf: Buffer.from('abcdef'), value: 'def', offset: -3, expected: 3 }, + { buf: Buffer.from('abcdef'), value: 'efgh', offset: 4, expected: -1 }, { buf: Buffer.from('abcdef'), value: '626364', encoding: 'hex', expected: 1 }, { buf: Buffer.from('abcdef'), value: '626364', encoding: 'utf-128', exception: 'TypeError: "utf-128" encoding is not supported' }, From noreply at nginx.com Thu Oct 10 16:44:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Thu, 10 Oct 2024 16:44:02 +0000 (UTC) Subject: [njs] Fixed Buffer.prototype.lastIndexOf() when `from` is provided. Message-ID: <20241010164402.E7FE94786B@pubserv1.nginx> details: https://github.com/nginx/njs/commit/e0563f4dd6a2ab52b4796b5cc536ce14e8ad6483 branches: master commit: e0563f4dd6a2ab52b4796b5cc536ce14e8ad6483 user: Dmitry Volyntsev date: Wed, 9 Oct 2024 17:59:26 -0700 description: Fixed Buffer.prototype.lastIndexOf() when `from` is provided. Previous incorrect fix was introduced in 5d15a8d6 (0.8.5). --- src/njs_buffer.c | 15 +++++++++------ src/qjs_buffer.c | 16 +++++++++------- test/buffer.t.js | 2 ++ 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/njs_buffer.c b/src/njs_buffer.c index 0bd5b896..20618d24 100644 --- a/src/njs_buffer.c +++ b/src/njs_buffer.c @@ -2218,13 +2218,12 @@ encoding: goto done; } - if (str.length > (size_t) length) { - goto done; - } - if (last) { - from -= str.length - 1; - from = njs_max(from, 0); + from = njs_min(from, length - (int64_t) str.length); + + if (to > from) { + goto done; + } } else { to -= str.length - 1; @@ -2246,6 +2245,10 @@ encoding: case NJS_NUMBER: byte = njs_number_to_uint32(njs_number(value)); + if (last) { + from = njs_min(from, length - 1); + } + for (i = from; i != to; i += increment) { if (u8[i] == byte) { index = i; diff --git a/src/qjs_buffer.c b/src/qjs_buffer.c index 5def5e63..8a7b10ff 100644 --- a/src/qjs_buffer.c +++ b/src/qjs_buffer.c @@ -1047,6 +1047,10 @@ qjs_buffer_prototype_index_of(JSContext *ctx, JSValueConst this_val, int argc, return JS_EXCEPTION; } + if (last) { + from = njs_min(from, length - 1); + } + for (i = from; i != to; i += increment) { if (self.start[i] == (uint8_t) byte) { return JS_NewInt32(ctx, i); @@ -1087,14 +1091,12 @@ encoding: return JS_NewInt32(ctx, (last) ? length : 0); } - if (str.length > (size_t) length) { - JS_FreeValue(ctx, buffer); - return JS_NewInt32(ctx, -1); - } - if (last) { - from -= str.length - 1; - from = njs_max(from, 0); + from = njs_min(from, length - (int64_t) str.length); + + if (to > from) { + goto done; + } } else { to -= str.length - 1; diff --git a/test/buffer.t.js b/test/buffer.t.js index f47c62f7..0b8db0d3 100644 --- a/test/buffer.t.js +++ b/test/buffer.t.js @@ -583,6 +583,7 @@ let lastIndexOf_tsuite = { { buf: Buffer.from('abcdef'), value: 'def', expected: 3 }, { buf: Buffer.from('abcdef'), value: 'abc', offset: 1, expected: 0 }, { buf: Buffer.from('abcdef'), value: 'def', offset: 1, expected: -1 }, + { buf: Buffer.from('xxxABCx'), value: 'ABC', offset: 3, expected: 3 }, { buf: Buffer.from(Buffer.alloc(7).fill('Zabcdef').buffer, 1), value: 'abcdef', expected: 0 }, { buf: Buffer.from(Buffer.alloc(7).fill('Zabcdef').buffer, 1), value: 'abcdefg', expected: -1 }, { buf: Buffer.from('abcdef'), value: '626364', encoding: 'hex', expected: 1 }, @@ -590,6 +591,7 @@ let lastIndexOf_tsuite = { exception: 'TypeError: "utf-128" encoding is not supported' }, { buf: Buffer.from('abcabc'), value: 0x61, expected: 3 }, { buf: Buffer.from('abcabc'), value: 0x61, offset: 1, expected: 0 }, + { buf: Buffer.from('ab'), value: 7, offset: 2, expected: -1 }, { buf: Buffer.from('abcdef'), value: Buffer.from('def'), expected: 3 }, { buf: Buffer.from('abcdef'), value: Buffer.from(new Uint8Array([0x60, 0x62, 0x63]).buffer, 1), expected: 1 }, { buf: Buffer.from('abcdef'), value: {}, From noreply at nginx.com Thu Oct 10 16:44:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Thu, 10 Oct 2024 16:44:02 +0000 (UTC) Subject: [njs] Test262: reporting name of the testsuite for a failed test. Message-ID: <20241010164402.DC5C747864@pubserv1.nginx> details: https://github.com/nginx/njs/commit/7b9ae6607b1bc9dae126a1a81ce1041bc79d8734 branches: master commit: 7b9ae6607b1bc9dae126a1a81ce1041bc79d8734 user: Dmitry Volyntsev date: Wed, 9 Oct 2024 16:05:26 -0700 description: Test262: reporting name of the testsuite for a failed test. --- test/harness/runTsuite.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/harness/runTsuite.js b/test/harness/runTsuite.js index aa3f5c0f..8103f661 100644 --- a/test/harness/runTsuite.js +++ b/test/harness/runTsuite.js @@ -30,7 +30,7 @@ async function run(tlist) { let r = results.map((r, i) => validate(ts.tests, r, i)); r.forEach((v, i) => { - assert.sameValue(v, true, `FAILED ${i}: ${JSON.stringify(ts.tests[i])}\n with reason: ${results[i].reason}`); + assert.sameValue(v, true, `FAILED ${ts.name}: ${JSON.stringify(ts.tests[i])}\n with reason: ${results[i].reason}`); }) } } From noreply at nginx.com Thu Oct 10 16:44:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Thu, 10 Oct 2024 16:44:02 +0000 (UTC) Subject: [njs] Fixed Buffer.prototype.indexOf() and friends. Message-ID: <20241010164402.EE6EE4786C@pubserv1.nginx> details: https://github.com/nginx/njs/commit/5a8bb2d172ad080efc0d719e7df609ac5355992e branches: master commit: 5a8bb2d172ad080efc0d719e7df609ac5355992e user: Dmitry Volyntsev date: Wed, 9 Oct 2024 18:25:52 -0700 description: Fixed Buffer.prototype.indexOf() and friends. With empty buffers in both the self and search cases. --- src/njs_buffer.c | 39 ++++++++------------------------------- src/qjs_buffer.c | 39 ++++++++------------------------------- test/buffer.t.js | 26 ++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 62 deletions(-) diff --git a/src/njs_buffer.c b/src/njs_buffer.c index 20618d24..2f0fc620 100644 --- a/src/njs_buffer.c +++ b/src/njs_buffer.c @@ -2117,10 +2117,6 @@ njs_buffer_prototype_index_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, index = -1; - if (njs_slow_path(array->byte_length == 0)) { - goto done; - } - length = array->byte_length; if (last) { @@ -2145,30 +2141,11 @@ njs_buffer_prototype_index_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, return ret; } - if (last) { - if (from >= 0) { - from = njs_min(from, length - 1); - - } else if (from < 0) { - from += length; - } - - if (from <= to) { - goto done; - } + if (from >= 0) { + from = njs_min(from, length); } else { - if (from < 0) { - from += length; - - if (from < 0) { - from = 0; - } - } - - if (from >= to) { - goto done; - } + from = njs_max(0, length + from); } } @@ -2213,11 +2190,6 @@ encoding: str.length = src->byte_length; } - if (njs_slow_path(str.length == 0)) { - index = (last) ? length : 0; - goto done; - } - if (last) { from = njs_min(from, length - (int64_t) str.length); @@ -2233,6 +2205,11 @@ encoding: } } + if (from == to && str.length == 0) { + index = 0; + goto done; + } + for (i = from; i != to; i += increment) { if (memcmp(&u8[i], str.start, str.length) == 0) { index = i; diff --git a/src/qjs_buffer.c b/src/qjs_buffer.c index 8a7b10ff..02618387 100644 --- a/src/qjs_buffer.c +++ b/src/qjs_buffer.c @@ -988,10 +988,6 @@ qjs_buffer_prototype_index_of(JSContext *ctx, JSValueConst this_val, int argc, length = self.length; - if (length == 0) { - return JS_NewInt32(ctx, -1); - } - if (last) { from = length - 1; to = -1; @@ -1015,30 +1011,11 @@ qjs_buffer_prototype_index_of(JSContext *ctx, JSValueConst this_val, int argc, return JS_EXCEPTION; } - if (last) { - if (from >= 0) { - from = njs_min(from, length - 1); - - } else if (from < 0) { - from += length; - } - - if (from <= to) { - return JS_NewInt32(ctx, -1); - } + if (from >= 0) { + from = njs_min(from, length); } else { - if (from < 0) { - from += length; - - if (from < 0) { - from = 0; - } - } - - if (from >= to) { - return JS_NewInt32(ctx, -1); - } + from = njs_max(0, length + from); } } @@ -1086,11 +1063,6 @@ encoding: "or Buffer-like object"); } - if (str.length == 0) { - JS_FreeValue(ctx, buffer); - return JS_NewInt32(ctx, (last) ? length : 0); - } - if (last) { from = njs_min(from, length - (int64_t) str.length); @@ -1106,6 +1078,11 @@ encoding: } } + if (from == to && str.length == 0) { + JS_FreeValue(ctx, buffer); + return JS_NewInt32(ctx, 0); + } + for (i = from; i != to; i += increment) { if (memcmp(&self.start[i], str.start, str.length) == 0) { JS_FreeValue(ctx, buffer); diff --git a/test/buffer.t.js b/test/buffer.t.js index 0b8db0d3..9becf487 100644 --- a/test/buffer.t.js +++ b/test/buffer.t.js @@ -474,6 +474,19 @@ let indexOf_tsuite = { { buf: Buffer.from('abcdef'), value: 'def', offset: 1, expected: 3 }, { buf: Buffer.from('abcdef'), value: 'def', offset: -3, expected: 3 }, { buf: Buffer.from('abcdef'), value: 'efgh', offset: 4, expected: -1 }, + { buf: Buffer.from(''), value: '', expected: 0 }, + { buf: Buffer.from(''), value: '', offset: -1, expected: 0 }, + { buf: Buffer.from(''), value: '', offset: 0, expected: 0 }, + { buf: Buffer.from(''), value: '', offset: 1, expected: 0 }, + { buf: Buffer.from('abc'), value: '', offset: -4, expected: 0 }, + { buf: Buffer.from('abc'), value: '', offset: -3, expected: 0 }, + { buf: Buffer.from('abc'), value: '', offset: -2, expected: 1 }, + { buf: Buffer.from('abc'), value: '', offset: -1, expected: 2 }, + { buf: Buffer.from('abc'), value: '', offset: 0, expected: 0 }, + { buf: Buffer.from('abc'), value: '', offset: 1, expected: 1 }, + { buf: Buffer.from('abc'), value: '', offset: 2, expected: 2 }, + { buf: Buffer.from('abc'), value: '', offset: 3, expected: 3 }, + { buf: Buffer.from('abc'), value: '', offset: 4, expected: 3 }, { buf: Buffer.from('abcdef'), value: '626364', encoding: 'hex', expected: 1 }, { buf: Buffer.from('abcdef'), value: '626364', encoding: 'utf-128', exception: 'TypeError: "utf-128" encoding is not supported' }, @@ -584,6 +597,19 @@ let lastIndexOf_tsuite = { { buf: Buffer.from('abcdef'), value: 'abc', offset: 1, expected: 0 }, { buf: Buffer.from('abcdef'), value: 'def', offset: 1, expected: -1 }, { buf: Buffer.from('xxxABCx'), value: 'ABC', offset: 3, expected: 3 }, + { buf: Buffer.from(''), value: '', expected: 0 }, + { buf: Buffer.from(''), value: '', offset: -1, expected: 0 }, + { buf: Buffer.from(''), value: '', offset: 0, expected: 0 }, + { buf: Buffer.from(''), value: '', offset: 1, expected: 0 }, + { buf: Buffer.from('abc'), value: '', offset: -4, expected: 0 }, + { buf: Buffer.from('abc'), value: '', offset: -3, expected: 0 }, + { buf: Buffer.from('abc'), value: '', offset: -2, expected: 1 }, + { buf: Buffer.from('abc'), value: '', offset: -1, expected: 2 }, + { buf: Buffer.from('abc'), value: '', offset: 0, expected: 0 }, + { buf: Buffer.from('abc'), value: '', offset: 1, expected: 1 }, + { buf: Buffer.from('abc'), value: '', offset: 2, expected: 2 }, + { buf: Buffer.from('abc'), value: '', offset: 3, expected: 3 }, + { buf: Buffer.from('abc'), value: '', offset: 4, expected: 3 }, { buf: Buffer.from(Buffer.alloc(7).fill('Zabcdef').buffer, 1), value: 'abcdef', expected: 0 }, { buf: Buffer.from(Buffer.alloc(7).fill('Zabcdef').buffer, 1), value: 'abcdefg', expected: -1 }, { buf: Buffer.from('abcdef'), value: '626364', encoding: 'hex', expected: 1 }, From noreply at nginx.com Fri Oct 11 01:28:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Fri, 11 Oct 2024 01:28:02 +0000 (UTC) Subject: [njs] Fixed Buffer.prototype.indexOf() on 32bits platforms. Message-ID: <20241011012802.41BDA47864@pubserv1.nginx> details: https://github.com/nginx/njs/commit/6902aaa17d8a2f30c44f2ed0ed165605233a2c6d branches: master commit: 6902aaa17d8a2f30c44f2ed0ed165605233a2c6d user: Dmitry Volyntsev date: Thu, 10 Oct 2024 16:33:13 -0700 description: Fixed Buffer.prototype.indexOf() on 32bits platforms. --- src/njs_buffer.c | 2 +- src/qjs_buffer.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/njs_buffer.c b/src/njs_buffer.c index 2f0fc620..3ce1b90a 100644 --- a/src/njs_buffer.c +++ b/src/njs_buffer.c @@ -2198,7 +2198,7 @@ encoding: } } else { - to -= str.length - 1; + to -= (int64_t) str.length - 1; if (from > to) { goto done; diff --git a/src/qjs_buffer.c b/src/qjs_buffer.c index 02618387..9f451e26 100644 --- a/src/qjs_buffer.c +++ b/src/qjs_buffer.c @@ -1071,7 +1071,7 @@ encoding: } } else { - to -= str.length - 1; + to -= (int64_t) str.length - 1; if (from > to) { goto done; From noreply at nginx.com Fri Oct 11 01:28:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Fri, 11 Oct 2024 01:28:02 +0000 (UTC) Subject: [njs] CI: added 32bit test in check-pr job. Message-ID: <20241011012802.3D7B947851@pubserv1.nginx> details: https://github.com/nginx/njs/commit/78a34bf74fea40e14c25bfc09f86cc96a81e5c5c branches: master commit: 78a34bf74fea40e14c25bfc09f86cc96a81e5c5c user: Dmitry Volyntsev date: Thu, 10 Oct 2024 18:03:38 -0700 description: CI: added 32bit test in check-pr job. --- .github/workflows/check-pr.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.github/workflows/check-pr.yml b/.github/workflows/check-pr.yml index 0160c1fd..6440e8ce 100644 --- a/.github/workflows/check-pr.yml +++ b/.github/workflows/check-pr.yml @@ -28,6 +28,12 @@ jobs: libgd-dev libxml2-dev libedit-dev libperl-dev libtest-harness-perl \ libgd-perl libgeoip-dev expect + - name: Install x86 build dependencies + run: | + sudo dpkg --add-architecture i386 + sudo apt-get update + sudo apt-get install -y gcc-multilib libc6:i386 libpcre2-dev:i386 zlib1g-dev:i386 + - name: Check out nginx run: | git clone https://github.com/nginx/nginx nginx-source @@ -55,6 +61,19 @@ jobs: $MAKE_UTILITY test $MAKE_UTILITY clean + - name: Configure and make njs, 32-bit + run: | + ./configure \ + --cc-opt="$CC_OPT -m32" \ + --ld-opt="$LD_OPT" \ + || cat build/autoconf.err + $MAKE_UTILITY -j$(nproc) + + - name: Test njs, 32-bit + run: | + $MAKE_UTILITY test + $MAKE_UTILITY clean + - name: Configure and make njs with quickjs run: | ./configure \ From noreply at nginx.com Mon Oct 14 23:52:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Mon, 14 Oct 2024 23:52:02 +0000 (UTC) Subject: [njs] Fixed dead store assignment in r.subrequest(). Message-ID: <20241014235202.5EC5D47E79@pubserv1.nginx> details: https://github.com/nginx/njs/commit/8759db7458f54d1d2c4d7657a68ca69c51a5c774 branches: master commit: 8759db7458f54d1d2c4d7657a68ca69c51a5c774 user: Dmitry Volyntsev date: Fri, 11 Oct 2024 18:41:27 -0700 description: Fixed dead store assignment in r.subrequest(). Found by Clang static analyzer. --- nginx/ngx_http_js_module.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/nginx/ngx_http_js_module.c b/nginx/ngx_http_js_module.c index c34fccbd..6f026293 100644 --- a/nginx/ngx_http_js_module.c +++ b/nginx/ngx_http_js_module.c @@ -3474,7 +3474,6 @@ ngx_http_js_ext_subrequest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, return NJS_ERROR; } - promise = 0; flags = NGX_HTTP_SUBREQUEST_BACKGROUND; njs_value_undefined_set(retval); @@ -5859,7 +5858,6 @@ ngx_http_qjs_ext_subrequest(JSContext *cx, JSValueConst this_val, "exclusive"); } - promise = 0; retval = JS_UNDEFINED; flags = NGX_HTTP_SUBREQUEST_BACKGROUND; From noreply at nginx.com Mon Oct 14 23:52:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Mon, 14 Oct 2024 23:52:02 +0000 (UTC) Subject: [njs] Modules: removed extra VMs creation when it is not needed. Message-ID: <20241014235202.59DA047E78@pubserv1.nginx> details: https://github.com/nginx/njs/commit/685b64f0326ac63a1deda3c9bdad61ad521990a7 branches: master commit: 685b64f0326ac63a1deda3c9bdad61ad521990a7 user: Dmitry Volyntsev date: Fri, 11 Oct 2024 17:23:42 -0700 description: Modules: removed extra VMs creation when it is not needed. Previously, a new VM instance was created for every location. This is not needed and consumes a lot of memory for large configurations. Instead, if no new js_import is introduced on the location level server level VM should be used. The issue was introduced in 9b674412 (0.8.6). This fixes #795 issue on Github. --- nginx/ngx_js.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nginx/ngx_js.c b/nginx/ngx_js.c index 98e48dc2..152045f0 100644 --- a/nginx/ngx_js.c +++ b/nginx/ngx_js.c @@ -3360,7 +3360,7 @@ ngx_js_merge_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf, ngx_js_named_path_t *import, *pi, *pij, *preload; if (conf->imports == NGX_CONF_UNSET_PTR - && conf->type == NGX_CONF_UNSET_UINT + && conf->type == prev->type && conf->paths == NGX_CONF_UNSET_PTR && conf->preload_objects == NGX_CONF_UNSET_PTR) { From noreply at nginx.com Tue Oct 15 14:19:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 15 Oct 2024 14:19:02 +0000 (UTC) Subject: [nginx] Configure: MSVC compatibility with PCRE2 10.43. Message-ID: <20241015141902.5BB3747E7A@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/b394d44cfa7e5c2d48a174d06f4b899b6cfd3ccf branches: master commit: b394d44cfa7e5c2d48a174d06f4b899b6cfd3ccf user: Thierry Bastian date: Wed, 9 Oct 2024 09:18:49 +0200 description: Configure: MSVC compatibility with PCRE2 10.43. --- auto/lib/pcre/make | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/auto/lib/pcre/make b/auto/lib/pcre/make index 839ef294b..182590ac5 100644 --- a/auto/lib/pcre/make +++ b/auto/lib/pcre/make @@ -36,7 +36,8 @@ if [ $PCRE_LIBRARY = PCRE2 ]; then pcre2_valid_utf.c \ pcre2_xclass.c" - ngx_pcre_test="pcre2_convert.c \ + ngx_pcre_test="pcre2_chkdint.c \ + pcre2_convert.c \ pcre2_extuni.c \ pcre2_find_bracket.c \ pcre2_script_run.c \ From noreply at nginx.com Wed Oct 16 16:28:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Wed, 16 Oct 2024 16:28:02 +0000 (UTC) Subject: [nginx] Branch created: security Message-ID: <20241016162802.23AD146C06@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/f9f2854043529262f84eacf0931f95f66cf930e8 branches: security commit: f9f2854043529262f84eacf0931f95f66cf930e8 user: Sergey Kandaurov date: Wed, 16 Oct 2024 20:22:52 +0400 description: Update SECURITY.md. Removed unrelated rewraps, minor editorial. No content changes. --- SECURITY.md | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index f4112303e..2479ca70e 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,25 +6,27 @@ implications of configurations and misconfigurations. ## Reporting a Vulnerability -Please report any vulnerabilities via one of the following methods (in order of -preference): +Please report any vulnerabilities via one of the following methods +(in order of preference): -1. [Report a vulnerability](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability) within this -repository. We are using the GitHub workflow that allows us to manage -vulnerabilities in a private manner and interact with reporters securely. +1. [Report a vulnerability](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability) +within this repository. We are using the GitHub workflow that allows us to +manage vulnerabilities in a private manner and interact with reporters +securely. 2. [Report directly to F5](https://www.f5.com/services/support/report-a-vulnerability). -3. Report via email to security-alert at nginx.org. This method will be deprecated -in the future. +3. Report via email to security-alert at nginx.org. +This method will be deprecated in the future. ### Vulnerability Disclosure and Fix Process The nginx community requests that all suspected vulnerabilities be reported -privately via the [Reporting a Vulnerability](SECURITY.md#reporting-a-vulnerability) guidelines. +privately via the +[Reporting a Vulnerability](SECURITY.md#reporting-a-vulnerability) guidelines. If a publicly released vulnerability is reported, we -may request to handle it according to the private disclosure process. If the -reporter agrees, we will follow the private disclosure process. +may request to handle it according to the private disclosure process. +If the reporter agrees, we will follow the private disclosure process. Security fixes will be applied to all supported stable releases, as well as the mainline version, as applicable. We recommend using the most recent mainline or @@ -45,7 +47,6 @@ private until made public. As nginx is supported by F5, we generally follow the disclosure. If an extension is needed, we will work with the disclosing person. - Publicly disclosed (i.e., Zero-Day vulnerabilities) will be addressed ASAP. - ## Confidentiality, Integrity, and Availability ### Confidentiality and Integrity @@ -97,6 +98,6 @@ recommended configurations to mitigate risks. ## Debug Logging and Core Files Debug logs and core files produced by nginx may contain un-sanitized data, -including sensitive information like client requests, server configurations, and -private key material. These artifacts must be handled carefully to avoid +including sensitive information like client requests, server configurations, +and private key material. These artifacts must be handled carefully to avoid exposing confidential data. From noreply at nginx.com Wed Oct 16 16:29:01 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Wed, 16 Oct 2024 16:29:01 +0000 (UTC) Subject: [nginx] Branch deleted: security Message-ID: <20241016162902.001E646C07@pubserv1.nginx> details: branches: security commit: f9f2854043529262f84eacf0931f95f66cf930e8 user: date: Wed, 16 Oct 2024 16:29:01 +0000 From noreply at nginx.com Thu Oct 17 05:42:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Thu, 17 Oct 2024 05:42:02 +0000 (UTC) Subject: [njs] CI: aligned asan build with with post-commit CI asan build. Message-ID: <20241017054202.1B14946C19@pubserv1.nginx> details: https://github.com/nginx/njs/commit/6ed6c100db944dc9d820897f8f0c582c245b2b48 branches: master commit: 6ed6c100db944dc9d820897f8f0c582c245b2b48 user: Dmitry Volyntsev date: Mon, 14 Oct 2024 23:12:16 -0700 description: CI: aligned asan build with with post-commit CI asan build. --- .github/workflows/check-pr.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/check-pr.yml b/.github/workflows/check-pr.yml index 6440e8ce..0c7d7f3e 100644 --- a/.github/workflows/check-pr.yml +++ b/.github/workflows/check-pr.yml @@ -110,10 +110,10 @@ jobs: leak:ngx_event_process_init EOF - - name: Configure and build nginx and njs modules with quickjs, static modules + - name: Configure and build nginx and njs modules with quickjs, asan, static modules run: | cd nginx-source - $NGINX_CONFIGURE_CMD --with-cc-opt="$CC_OPT -I${{ github.workspace }}/quickjs -fsanitize=address" --with-ld-opt="$LD_OPT -L${{ github.workspace }}/quickjs -fsanitize=address" --add-module=../nginx || cat objs/autoconf.err + $NGINX_CONFIGURE_CMD --with-cc-opt="$CC_OPT -I${{ github.workspace }}/quickjs -fsanitize=address -DNJS_DEBUG_MEMORY -DNGX_DEBUG_PALLOC -DNGX_DEBUG_MALLOC" --with-ld-opt="$LD_OPT -L${{ github.workspace }}/quickjs -fsanitize=address" --add-module=../nginx || cat objs/autoconf.err $MAKE_UTILITY -j$(nproc) - name: Test njs modules, static modules @@ -138,10 +138,10 @@ jobs: ASAN_OPTIONS: "detect_odr_violation=0:report_globals=0" LSAN_OPTIONS: "suppressions=${{ github.workspace }}/lsan_suppressions.txt" - - name: Configure and build nginx and njs modules with quickjs, dynamic modules + - name: Configure and build nginx and njs modules with quickjs, asan, dynamic modules run: | cd nginx-source - $NGINX_CONFIGURE_CMD --with-debug --with-cc-opt="$CC_OPT -I${{ github.workspace }}/quickjs -fsanitize=address" --with-ld-opt="$LD_OPT -L${{ github.workspace }}/quickjs -fsanitize=address" --add-dynamic-module=../nginx || cat objs/autoconf.err + $NGINX_CONFIGURE_CMD --with-debug --with-cc-opt="$CC_OPT -I${{ github.workspace }}/quickjs -fsanitize=address -DNJS_DEBUG_MEMORY -DNGX_DEBUG_PALLOC -DNGX_DEBUG_MALLOC" --with-ld-opt="$LD_OPT -L${{ github.workspace }}/quickjs -fsanitize=address" --add-dynamic-module=../nginx || cat objs/autoconf.err $MAKE_UTILITY -j$(nproc) modules $MAKE_UTILITY -j$(nproc) From noreply at nginx.com Thu Oct 17 05:42:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Thu, 17 Oct 2024 05:42:02 +0000 (UTC) Subject: [njs] Implemented lazy stack symbolization. Message-ID: <20241017054202.1F87A46C1A@pubserv1.nginx> details: https://github.com/nginx/njs/commit/eda6fa7d430619f646a2c969a267225306cff521 branches: master commit: eda6fa7d430619f646a2c969a267225306cff521 user: Dmitry Volyntsev date: Tue, 15 Oct 2024 18:28:19 -0700 description: Implemented lazy stack symbolization. Previously, when an exception was thrown, the exception got 'stack' property attached which contained the backtrace information about where the exception happened. This could be a heavy operation and it was not always needed. To optimize it, the process is split into 2 phases. The first phase collects all the necessary info about the current stack. The second phase, where the stack symbolization happens, occurs only when this property is referenced. --- src/njs_error.c | 182 +++++++++++++++++++++++++++++++++++------------ src/njs_vm.h | 2 +- src/test/njs_benchmark.c | 11 +++ 3 files changed, 150 insertions(+), 45 deletions(-) diff --git a/src/njs_error.c b/src/njs_error.c index 376205d7..6a14d767 100644 --- a/src/njs_error.c +++ b/src/njs_error.c @@ -8,6 +8,15 @@ #include +typedef struct { + union { + njs_function_t *function; + u_char *pc; + } u; + uint8_t native; +} njs_stack_entry_t; + + typedef struct { njs_str_t name; njs_str_t file; @@ -16,7 +25,7 @@ typedef struct { static njs_int_t njs_add_backtrace_entry(njs_vm_t *vm, njs_arr_t *stack, - njs_native_frame_t *native_frame); + njs_stack_entry_t *se); static njs_int_t njs_backtrace_to_string(njs_vm_t *vm, njs_arr_t *backtrace, njs_str_t *dst); @@ -87,22 +96,13 @@ njs_error_fmt_new(njs_vm_t *vm, njs_value_t *dst, njs_object_type_t type, static njs_int_t -njs_error_stack_new(njs_vm_t *vm, njs_object_t *error, njs_value_t *retval) +njs_error_stack_new(njs_vm_t *vm, njs_object_value_t *error) { - njs_int_t ret; - njs_str_t string; njs_arr_t *stack; - njs_value_t value; + njs_stack_entry_t *se; njs_native_frame_t *frame; - njs_set_object(&value, error); - - ret = njs_error_to_string(vm, retval, &value); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - - stack = njs_arr_create(vm->mem_pool, 4, sizeof(njs_backtrace_entry_t)); + stack = njs_arr_create(vm->mem_pool, 4, sizeof(njs_stack_entry_t)); if (njs_slow_path(stack == NULL)) { return NJS_ERROR; } @@ -110,10 +110,20 @@ njs_error_stack_new(njs_vm_t *vm, njs_object_t *error, njs_value_t *retval) frame = vm->top_frame; for ( ;; ) { - if ((frame->native || frame->pc != NULL) - && njs_add_backtrace_entry(vm, stack, frame) != NJS_OK) - { - break; + if (frame->native || frame->pc != NULL) { + se = njs_arr_add(stack); + if (njs_slow_path(se == NULL)) { + return NJS_ERROR; + } + + se->native = frame->native; + + if (se->native) { + se->u.function = frame->function; + + } else { + se->u.pc = frame->pc; + } } frame = frame->previous; @@ -123,25 +133,16 @@ njs_error_stack_new(njs_vm_t *vm, njs_object_t *error, njs_value_t *retval) } } - njs_string_get(retval, &string); - - ret = njs_backtrace_to_string(vm, stack, &string); - - njs_arr_destroy(stack); - - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } + njs_data(&error->value) = stack; - return njs_string_create(vm, retval, string.start, string.length); + return NJS_OK; } njs_int_t njs_error_stack_attach(njs_vm_t *vm, njs_value_t value) { - njs_int_t ret; - njs_value_t stack; + njs_int_t ret; if (njs_slow_path(!njs_is_error(&value)) || njs_object(&value)->stack_attached) @@ -153,7 +154,7 @@ njs_error_stack_attach(njs_vm_t *vm, njs_value_t value) return NJS_OK; } - ret = njs_error_stack_new(vm, njs_object(&value), &stack); + ret = njs_error_stack_new(vm, value.data.u.object_value); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "njs_error_stack_new() failed"); return NJS_ERROR; @@ -161,10 +162,7 @@ njs_error_stack_attach(njs_vm_t *vm, njs_value_t value) njs_object(&value)->stack_attached = 1; - return njs_object_prop_define(vm, &value, - njs_value_arg(&njs_error_stack_string), - &stack, NJS_OBJECT_PROP_VALUE_CW, - NJS_STACK_HASH); + return NJS_OK; } @@ -194,16 +192,20 @@ njs_error_alloc(njs_vm_t *vm, njs_object_t *proto, const njs_value_t *name, njs_int_t ret; njs_object_t *error; njs_object_prop_t *prop; + njs_object_value_t *ov; njs_lvlhsh_query_t lhq; - error = njs_mp_alloc(vm->mem_pool, sizeof(njs_object_t)); - if (njs_slow_path(error == NULL)) { + ov = njs_mp_alloc(vm->mem_pool, sizeof(njs_object_value_t)); + if (njs_slow_path(ov == NULL)) { goto memory_error; } + njs_set_data(&ov->value, NULL, NJS_DATA_TAG_ANY); + + error = &ov->object; njs_lvlhsh_init(&error->hash); njs_lvlhsh_init(&error->shared_hash); - error->type = NJS_OBJECT; + error->type = NJS_OBJECT_VALUE; error->shared = 0; error->extensible = 1; error->fast_array = 0; @@ -485,15 +487,18 @@ const njs_object_init_t njs_aggregate_error_constructor_init = { void njs_memory_error_set(njs_vm_t *vm, njs_value_t *value) { - njs_object_t *object; + njs_object_t *object; + njs_object_value_t *ov; - object = &vm->memory_error_object; + ov = &vm->memory_error_object; + njs_set_data(&ov->value, NULL, NJS_DATA_TAG_ANY); + object = &ov->object; njs_lvlhsh_init(&object->hash); njs_lvlhsh_init(&object->shared_hash); object->__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_INTERNAL_ERROR); object->slots = NULL; - object->type = NJS_OBJECT; + object->type = NJS_OBJECT_VALUE; object->shared = 1; /* @@ -691,6 +696,92 @@ njs_error_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, } +static njs_int_t +njs_error_prototype_stack(njs_vm_t *vm, njs_object_prop_t *prop, + njs_value_t *value, njs_value_t *setval, njs_value_t *retval) +{ + njs_int_t ret; + njs_str_t string; + njs_arr_t *stack, *backtrace; + njs_uint_t i; + njs_value_t rv, *stackval; + njs_stack_entry_t *se; + + if (retval != NULL) { + if (!njs_is_error(value)) { + njs_set_undefined(retval); + return NJS_DECLINED; + } + + stackval = njs_object_value(value); + + if (setval != NULL) { + njs_value_assign(stackval, setval); + return NJS_OK; + } + + if (!njs_is_data(stackval, NJS_DATA_TAG_ANY)) { + njs_value_assign(retval, stackval); + return NJS_OK; + } + + stack = njs_data(stackval); + if (stack == NULL) { + njs_set_undefined(retval); + return NJS_OK; + } + + se = stack->start; + + backtrace = njs_arr_create(vm->mem_pool, stack->items, + sizeof(njs_backtrace_entry_t)); + if (njs_slow_path(backtrace == NULL)) { + return NJS_ERROR; + } + + for (i = 0; i < stack->items; i++) { + if (njs_add_backtrace_entry(vm, backtrace, &se[i]) != NJS_OK) { + return NJS_ERROR; + } + } + + ret = njs_error_to_string2(vm, &rv, value, 0); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + njs_string_get(&rv, &string); + + ret = njs_backtrace_to_string(vm, backtrace, &string); + + njs_arr_destroy(backtrace); + njs_arr_destroy(stack); + + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + ret = njs_string_create(vm, stackval, string.start, string.length); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + njs_value_assign(retval, stackval); + + return NJS_OK; + } + + /* Delete. */ + + if (njs_is_error(value)) { + stackval = njs_object_value(value); + njs_set_data(stackval, NULL, NJS_DATA_TAG_ANY); + } + + return NJS_OK; +} + + njs_int_t njs_error_to_string(njs_vm_t *vm, njs_value_t *retval, const njs_value_t *error) { @@ -717,6 +808,9 @@ static const njs_object_prop_t njs_error_prototype_properties[] = NJS_DECLARE_PROP_NATIVE("valueOf", njs_error_prototype_value_of, 0, 0), NJS_DECLARE_PROP_NATIVE("toString", njs_error_prototype_to_string, 0, 0), + + NJS_DECLARE_PROP_HANDLER("stack", njs_error_prototype_stack, + 0, 0, NJS_OBJECT_PROP_VALUE_CW), }; @@ -986,14 +1080,14 @@ const njs_object_type_init_t njs_aggregate_error_type_init = { static njs_int_t njs_add_backtrace_entry(njs_vm_t *vm, njs_arr_t *stack, - njs_native_frame_t *native_frame) + njs_stack_entry_t *se) { njs_int_t ret; njs_vm_code_t *code; njs_function_t *function; njs_backtrace_entry_t *be; - function = native_frame->function; + function = se->native ? se->u.function : NULL; if (function != NULL && function->bound != NULL) { /* Skip. */ @@ -1019,7 +1113,7 @@ njs_add_backtrace_entry(njs_vm_t *vm, njs_arr_t *stack, return NJS_OK; } - code = njs_lookup_code(vm, native_frame->pc); + code = njs_lookup_code(vm, se->u.pc); if (code != NULL) { be->name = code->name; @@ -1028,7 +1122,7 @@ njs_add_backtrace_entry(njs_vm_t *vm, njs_arr_t *stack, be->name = njs_entry_anonymous; } - be->line = njs_lookup_line(code->lines, native_frame->pc - code->start); + be->line = njs_lookup_line(code->lines, se->u.pc - code->start); if (!vm->options.quiet) { be->file = code->file; } diff --git a/src/njs_vm.h b/src/njs_vm.h index 6d09caf8..a3f2b5a9 100644 --- a/src/njs_vm.h +++ b/src/njs_vm.h @@ -165,7 +165,7 @@ struct njs_vm_s { * MemoryError is statically allocated immutable Error object * with the InternalError prototype. */ - njs_object_t memory_error_object; + njs_object_value_t memory_error_object; njs_object_t string_object; njs_object_t global_object; diff --git a/src/test/njs_benchmark.c b/src/test/njs_benchmark.c index 6ca83e82..98c618c8 100644 --- a/src/test/njs_benchmark.c +++ b/src/test/njs_benchmark.c @@ -116,6 +116,7 @@ njs_benchmark_test(njs_vm_t *parent, njs_opts_t *opts, njs_value_t *report, njs_vm_opt_init(&options); + options.backtrace = 1; options.addons = njs_benchmark_addon_external_modules; vm = NULL; @@ -468,6 +469,16 @@ static njs_benchmark_test_t njs_test[] = njs_str("$shared.method('YES')"), njs_str("shared"), 1000 }, + + { "exception", + njs_str("function f() { try { throw new Error('test') } catch (e) { return e.message } } [f].map(v=>v())[0]"), + njs_str("test"), + 10000 }, + + { "exception.stack", + njs_str("function f() { try { throw new Error('test') } catch (e) { return e.stack } } [f].map(v=>v())[0]"), + njs_str("Error: test\n at f (:1)\n at anonymous (:1)\n at Array.prototype.map (native)\n at main (:1)\n"), + 100 }, }; From noreply at nginx.com Fri Oct 18 05:22:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Fri, 18 Oct 2024 05:22:02 +0000 (UTC) Subject: [njs] HTTP: added strict check for js_body_filter syntax. Message-ID: <20241018052202.53AFD46C35@pubserv1.nginx> details: https://github.com/nginx/njs/commit/6ed70de1607fcf137cdfc120f68967f0bfd46829 branches: master commit: 6ed70de1607fcf137cdfc120f68967f0bfd46829 user: Dmitry Volyntsev date: Thu, 17 Oct 2024 18:31:26 -0700 description: HTTP: added strict check for js_body_filter syntax. --- nginx/ngx_http_js_module.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/nginx/ngx_http_js_module.c b/nginx/ngx_http_js_module.c index 6f026293..06429241 100644 --- a/nginx/ngx_http_js_module.c +++ b/nginx/ngx_http_js_module.c @@ -7921,20 +7921,24 @@ ngx_http_js_body_filter_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) jlcf->buffer_type = NGX_JS_STRING; - if (cf->args->nelts == 3 - && ngx_strncmp(value[2].data, "buffer_type=", 12) == 0) - { - if (ngx_strcmp(&value[2].data[12], "string") == 0) { - jlcf->buffer_type = NGX_JS_STRING; + if (cf->args->nelts == 3) { + if (ngx_strncmp(value[2].data, "buffer_type=", 12) == 0) { + if (ngx_strcmp(&value[2].data[12], "string") == 0) { + jlcf->buffer_type = NGX_JS_STRING; - } else if (ngx_strcmp(&value[2].data[12], "buffer") == 0) { - jlcf->buffer_type = NGX_JS_BUFFER; + } else if (ngx_strcmp(&value[2].data[12], "buffer") == 0) { + jlcf->buffer_type = NGX_JS_BUFFER; + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid buffer_type value \"%V\", " + "it must be \"string\" or \"buffer\"", + &value[2]); + return NGX_CONF_ERROR; + } } else { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid buffer_type value \"%V\", " - "it must be \"string\" or \"buffer\"", - &value[2]); + "invalid parameter \"%V\"", &value[2]); return NGX_CONF_ERROR; } } From noreply at nginx.com Fri Oct 18 05:22:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Fri, 18 Oct 2024 05:22:02 +0000 (UTC) Subject: [njs] Tests: added js_body_filter test with non-UTF8 data. Message-ID: <20241018052202.5803246C36@pubserv1.nginx> details: https://github.com/nginx/njs/commit/198539036deacc8d263005a14849fd93c5d314f4 branches: master commit: 198539036deacc8d263005a14849fd93c5d314f4 user: Dmitry Volyntsev date: Thu, 17 Oct 2024 18:44:17 -0700 description: Tests: added js_body_filter test with non-UTF8 data. To test for implicit UTF8 conversion. --- nginx/t/js_body_filter.t | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/nginx/t/js_body_filter.t b/nginx/t/js_body_filter.t index b90a2502..679b6cba 100644 --- a/nginx/t/js_body_filter.t +++ b/nginx/t/js_body_filter.t @@ -55,6 +55,11 @@ http { proxy_pass http://127.0.0.1:8081/source; } + location /buffer_type_nonutf8 { + js_body_filter test.buffer_type buffer_type=buffer; + proxy_pass http://127.0.0.1:8081/nonutf8_source; + } + location /forward { js_body_filter test.forward buffer_type=string; proxy_pass http://127.0.0.1:8081/source; @@ -80,6 +85,11 @@ http { postpone_output 1; js_content test.source; } + + location /nonutf8_source { + postpone_output 1; + js_content test.nonutf8_source; + } } } @@ -128,6 +138,17 @@ $t->write_file('test.js', <Buffer.from(v, 'hex')); + chunks.delay = 5; + chunks.r = r; + chunks.chain = chain; + + r.status = 200; + r.sendHeader(); + chain(chunks, 0); + } + function filter(r, data, flags) { if (flags.last || data.length >= Number(r.args.len)) { r.sendBuffer(`\${data}|`, flags); @@ -149,16 +170,18 @@ $t->write_file('test.js', <try_run('no njs body filter')->plan(6); +$t->try_run('no njs body filter')->plan(7); ############################################################################### like(http_get('/append'), qr/AAABBCDDDDXXX/, 'append'); like(http_get('/buffer_type'), qr/AAABBCDDDD/, 'buffer type'); +like(http_get('/buffer_type_nonutf8'), qr/\xaa\xaa\xbb\xcc\xdd\xdd/, + 'buffer type nonutf8'); like(http_get('/forward'), qr/AAABBCDDDD/, 'forward'); like(http_get('/filter?len=3'), qr/AAA|DDDD|/, 'filter 3'); like(http_get('/filter?len=2&dup=1'), qr/AAA|AAABB|BBDDDD|DDDD/, From noreply at nginx.com Mon Oct 21 15:44:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Mon, 21 Oct 2024 15:44:02 +0000 (UTC) Subject: [nginx] Updated security policy to include disclosure details. Message-ID: <20241021154402.38B4E4787D@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/f45c2707ea1bf6fde3bd0c045cc4a63cdc4a966a branches: master commit: f45c2707ea1bf6fde3bd0c045cc4a63cdc4a966a user: jzebor-at-f5 <144731595+jzebor-at-f5 at users.noreply.github.com> date: Fri, 11 Oct 2024 09:48:53 -0700 description: Updated security policy to include disclosure details. --- SECURITY.md | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 89 insertions(+), 5 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index 2b48e47e3..f5cfcd788 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,8 +1,8 @@ # Security Policy -## Latest Versions - -We advise users to run the most recent mainline or stable release of nginx. +This document provides an overview of security concerns related to nginx +deployments, focusing on confidentiality, integrity, availability, and the +implications of configurations and misconfigurations. ## Reporting a Vulnerability @@ -10,11 +10,95 @@ Please report any vulnerabilities via one of the following methods (in order of preference): 1. [Report a vulnerability](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability) -within this repository. We are using the Github workflow that allows us to -manage vulnerabilities in a private manner and to interact with reporters +within this repository. We are using the GitHub workflow that allows us to +manage vulnerabilities in a private manner and interact with reporters securely. 2. [Report directly to F5](https://www.f5.com/services/support/report-a-vulnerability). 3. Report via email to security-alert at nginx.org. This method will be deprecated in the future. + +### Vulnerability Disclosure and Fix Process + +The nginx team expects that all suspected vulnerabilities be reported +privately via the +[Reporting a Vulnerability](SECURITY.md#reporting-a-vulnerability) guidelines. +If a publicly released vulnerability is reported, we +may request to handle it according to the private disclosure process. +If the reporter agrees, we will follow the private disclosure process. + +Security fixes will be applied to all supported stable releases, as well +as the mainline version, as applicable. We recommend using the most recent +mainline or stable release of nginx. Fixes are created and tested by the core +team using a GitHub private fork for security. If necessary, the reporter +may be invited to contribute to the fork and assist with the solution. + +The nginx team is committed to responsible information disclosure with +sufficient detail, such as the CVSS score and vector. Privately disclosed +vulnerabilities are embargoed by default until the fix is released. +Communications and fixes remain private until made public. As nginx is +supported by F5, we generally follow the +[F5 security vulnerability response policy](https://my.f5.com/manage/s/article/K4602). + +### Vulnerability Disclosure and Fix Service Level Objectives + +- We will acknowledge all vulnerability reports within 1 to 3 days. +- Fixes will be developed and released within 90 days from the date of +disclosure. If an extension is needed, we will work with the disclosing person. +- Publicly disclosed (i.e., Zero-Day vulnerabilities) will be addressed ASAP. + +## Confidentiality, Integrity, and Availability + +### Confidentiality and Integrity + +Vulnerabilities compromising data confidentiality or integrity are considered +the highest priority. Any issue leading to unauthorized data access, leaks, or +manipulation will trigger the security release process. + +### Availability + +Availability issues must meet the following criteria to trigger the security +release process: +- Is present in a standard module included with nginx. +- Arises from traffic that the module is designed to handle. +- Resource exhaustion issues are not mitigated by existing timeout, rate +limiting, or buffer size configurations, or applying changes is impractical. +- Results in highly asymmetric, extreme resource consumption. + +Availability issues excluded from the security release process: +- Local file content or upstream response content resulting only in worker +process termination. +- Issues with experimental features which result only in worker process +termination. + +## Trusted Configurations and Misconfigurations + +In nginx, configuration files, modules, certificate/key pairs, nginx JavaScript, +and local file content are considered trusted sources. Issues arising from +loading or execution of these trusted components are not considered +vulnerabilities. Operators are responsible for securing and maintaining the +integrity of these sources. Misconfigurations can create vulnerabilities, and +operators should implement configurations according to best practices, review +them regularly, and apply security updates. + +## Data Plane vs. Control Plane + +The data plane handles traffic through nginx, directly interacting with user +data. nginx inherently trusts the content and instructions from upstream +servers. The control plane governs configuration, management, and orchestration. +Misconfigurations or vulnerabilities in the control plane can cause improper +behavior in the data plane. + +## Modules Under Scope + +The policy applies to all nginx modules included in this repository. Security +considerations and attack vectors for each module will be identified, with +recommended configurations to mitigate risks. + +## Debug Logging and Core Files + +Debug logs and core files produced by nginx may contain un-sanitized data, +including sensitive information like client requests, server configurations, +and private key material. These artifacts must be handled carefully to avoid +exposing confidential data. From noreply at nginx.com Mon Oct 21 23:53:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Mon, 21 Oct 2024 23:53:02 +0000 (UTC) Subject: [njs] FS: introduced fs.readlinkSync() and friends. Message-ID: <20241021235302.238054787F@pubserv1.nginx> details: https://github.com/nginx/njs/commit/1b2a339162817da195a3d8e631b61233c1aa57f0 branches: master commit: 1b2a339162817da195a3d8e631b61233c1aa57f0 user: Dmitry Volyntsev date: Fri, 18 Oct 2024 22:01:58 -0700 description: FS: introduced fs.readlinkSync() and friends. This closes #802 feature request on Github. --- external/njs_fs_module.c | 128 +++++++++++++++++++++++++++++++++++++++++++++++ test/fs/methods.t.js | 53 ++++++++++++++++++++ 2 files changed, 181 insertions(+) diff --git a/external/njs_fs_module.c b/external/njs_fs_module.c index de378cee..f5221883 100644 --- a/external/njs_fs_module.c +++ b/external/njs_fs_module.c @@ -160,6 +160,8 @@ static njs_int_t njs_fs_read_file(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval); static njs_int_t njs_fs_readdir(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval); +static njs_int_t njs_fs_readlink(njs_vm_t *vm, njs_value_t *args, + njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval); static njs_int_t njs_fs_realpath(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval); static njs_int_t njs_fs_rename(njs_vm_t *vm, njs_value_t *args, @@ -415,6 +417,17 @@ static njs_external_t njs_ext_fs_promises[] = { } }, + { + .flags = NJS_EXTERN_METHOD, + .name.string = njs_str("readlink"), + .writable = 1, + .configurable = 1, + .u.method = { + .native = njs_fs_readlink, + .magic8 = NJS_FS_PROMISE, + } + }, + { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("realpath"), @@ -726,6 +739,28 @@ static njs_external_t njs_ext_fs[] = { } }, + { + .flags = NJS_EXTERN_METHOD, + .name.string = njs_str("readlink"), + .writable = 1, + .configurable = 1, + .u.method = { + .native = njs_fs_readlink, + .magic8 = NJS_FS_CALLBACK, + } + }, + + { + .flags = NJS_EXTERN_METHOD, + .name.string = njs_str("readlinkSync"), + .writable = 1, + .configurable = 1, + .u.method = { + .native = njs_fs_readlink, + .magic8 = NJS_FS_DIRECT, + } + }, + { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("realpath"), @@ -2035,6 +2070,99 @@ done: } +static njs_int_t +njs_fs_readlink(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t calltype, njs_value_t *retval) +{ + ssize_t n; + njs_int_t ret; + njs_str_t s; + const char *path; + njs_value_t *callback, *options; + njs_opaque_value_t encode, result; + const njs_buffer_encoding_t *encoding; + char path_buf[NJS_MAX_PATH + 1], + dst_buf[NJS_MAX_PATH + 1]; + + path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path"); + if (njs_slow_path(path == NULL)) { + return NJS_ERROR; + } + + callback = NULL; + options = njs_arg(args, nargs, 2); + + if (calltype == NJS_FS_CALLBACK) { + callback = njs_arg(args, nargs, njs_min(nargs - 1, 3)); + if (!njs_value_is_function(callback)) { + njs_vm_type_error(vm, "\"callback\" must be a function"); + return NJS_ERROR; + } + + if (options == callback) { + options = njs_value_arg(&njs_value_undefined); + } + } + + njs_value_undefined_set(njs_value_arg(&encode)); + + if (njs_value_is_string(options)) { + njs_value_assign(&encode, options); + + } else if (!njs_value_is_undefined(options)) { + if (!njs_value_is_object(options)) { + njs_vm_type_error(vm, "Unknown options type " + "(a string or object required)"); + return NJS_ERROR; + } + + (void) njs_vm_object_prop(vm, options, &string_encoding, &encode); + } + + encoding = NULL; + + if (njs_value_is_string(njs_value_arg(&encode))) { + njs_value_string_get(njs_value_arg(&encode), &s); + + } else { + s.length = 0; + s.start = NULL; + } + + if (!njs_strstr_eq(&s, &string_buffer)) { + encoding = njs_buffer_encoding(vm, njs_value_arg(&encode), 1); + if (njs_slow_path(encoding == NULL)) { + return NJS_ERROR; + } + } + + s.start = (u_char *) dst_buf; + n = readlink(path, dst_buf, sizeof(dst_buf) - 1); + if (njs_slow_path(n < 0)) { + ret = njs_fs_error(vm, "readlink", strerror(errno), path, errno, + &result); + goto done; + } + + s.length = n; + + if (encoding == NULL) { + ret = njs_buffer_new(vm, njs_value_arg(&result), s.start, s.length); + + } else { + ret = encoding->encode(vm, njs_value_arg(&result), &s); + } + +done: + + if (ret == NJS_OK) { + return njs_fs_result(vm, &result, calltype, callback, 2, retval); + } + + return NJS_ERROR; +} + + static njs_int_t njs_fs_realpath(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval) diff --git a/test/fs/methods.t.js b/test/fs/methods.t.js index 1d177e18..72afb494 100644 --- a/test/fs/methods.t.js +++ b/test/fs/methods.t.js @@ -435,6 +435,56 @@ let realpathP_tsuite = { get tests() { return realpath_tests() }, }; +async function readlink_test(params) { + let lname = params.args[0]; + try { fs.unlinkSync(lname); } catch (e) {} + fs.symlinkSync("test/fs/ascii", lname); + + let data = await method("readlink", params); + + if (!params.check(data)) { + throw Error(`readlink failed check`); + } + + return 'SUCCESS'; +} + +let readlink_tests = () => [ + { args: [`${test_dir}/symlink`], + check: (data) => data.endsWith("test/fs/ascii") }, + { args: [`${test_dir}/symlink`, {encoding:'buffer'}], + check: (data) => data instanceof Buffer }, + { args: [`${test_dir}/symlink`, {encoding:'hex'}], + check: (data) => data.endsWith("746573742f66732f6173636969") }, +]; + +let readlink_tsuite = { + name: "fs readlink", + skip: () => (!has_fs() || !has_buffer()), + T: readlink_test, + prepare_args: p, + opts: { type: "callback" }, + get tests() { return readlink_tests() }, +}; + +let readlinkSync_tsuite = { + name: "fs readlinkSync", + skip: () => (!has_fs() || !has_buffer()), + T: readlink_test, + prepare_args: p, + opts: { type: "sync" }, + get tests() { return readlink_tests() }, +}; + +let readlinkP_tsuite = { + name: "fsp readlink", + skip: () => (!has_fs() || !has_buffer()), + T: readlink_test, + prepare_args: p, + opts: { type: "promise" }, + get tests() { return readlink_tests() }, +}; + async function method_test(params) { if (params.init) { params.init(params); @@ -1190,6 +1240,9 @@ run([ realpath_tsuite, realpathSync_tsuite, realpathP_tsuite, + readlink_tsuite, + readlinkSync_tsuite, + readlinkP_tsuite, stat_tsuite, statSync_tsuite, statP_tsuite, From noreply at nginx.com Tue Oct 22 01:07:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 22 Oct 2024 01:07:02 +0000 (UTC) Subject: [njs] Improved error messages for module loading failures. Message-ID: <20241022010702.D0070476A3@pubserv1.nginx> details: https://github.com/nginx/njs/commit/ed36e94242de38e88a6cc536404609fc72bf6456 branches: master commit: ed36e94242de38e88a6cc536404609fc72bf6456 user: Dmitry Volyntsev date: Fri, 18 Oct 2024 18:24:49 -0700 description: Improved error messages for module loading failures. There are several reasons why a file cannot be opened. Without extra information, especially in containerized environments, these problems are difficult to debug. Adding errno status to the error output helps identify the root cause. Additionally, error messages are now aligned between njs and QuickJS. --- nginx/ngx_js.c | 350 ++++++++++++++++++++++++++++++++++++++++++++++- nginx/ngx_js.h | 1 + src/njs.h | 2 + src/njs_module.c | 2 +- src/njs_parser.c | 9 +- src/test/njs_unit_test.c | 4 +- test/shell_test_njs.exp | 4 +- 7 files changed, 362 insertions(+), 10 deletions(-) diff --git a/nginx/ngx_js.c b/nginx/ngx_js.c index 152045f0..f70288cf 100644 --- a/nginx/ngx_js.c +++ b/nginx/ngx_js.c @@ -1979,10 +1979,15 @@ ngx_qjs_module_loader(JSContext *cx, const char *module_name, void *opaque) info.name.start = (u_char *) module_name; info.name.length = njs_strlen(module_name); + errno = 0; ret = ngx_js_module_lookup(conf, &info); if (ret != NJS_OK) { - JS_ThrowReferenceError(cx, "could not load module filename '%s'", - module_name); + if (errno != 0) { + JS_ThrowReferenceError(cx, "Cannot load module \"%s\" " + "(%s:%s)", module_name, + ngx_js_errno_string(errno), strerror(errno)); + } + return NULL; } @@ -3764,8 +3769,14 @@ ngx_js_module_loader(njs_vm_t *vm, njs_external_ptr_t external, njs_str_t *name) info.name = *name; + errno = 0; ret = ngx_js_module_lookup(conf, &info); if (njs_slow_path(ret != NJS_OK)) { + if (errno != 0) { + njs_vm_ref_error(vm, "Cannot load module \"%V\" (%s:%s)", name, + ngx_js_errno_string(errno), strerror(errno)); + } + return NULL; } @@ -4076,6 +4087,341 @@ ngx_js_monotonic_time(void) } +#define ngx_js_errno_case(e) \ + case e: \ + return #e; + + +const char* +ngx_js_errno_string(int errnum) +{ + switch (errnum) { +#ifdef EACCES + ngx_js_errno_case(EACCES); +#endif + +#ifdef EADDRINUSE + ngx_js_errno_case(EADDRINUSE); +#endif + +#ifdef EADDRNOTAVAIL + ngx_js_errno_case(EADDRNOTAVAIL); +#endif + +#ifdef EAFNOSUPPORT + ngx_js_errno_case(EAFNOSUPPORT); +#endif + +#ifdef EAGAIN + ngx_js_errno_case(EAGAIN); +#endif + +#ifdef EWOULDBLOCK +#if EAGAIN != EWOULDBLOCK + ngx_js_errno_case(EWOULDBLOCK); +#endif +#endif + +#ifdef EALREADY + ngx_js_errno_case(EALREADY); +#endif + +#ifdef EBADF + ngx_js_errno_case(EBADF); +#endif + +#ifdef EBADMSG + ngx_js_errno_case(EBADMSG); +#endif + +#ifdef EBUSY + ngx_js_errno_case(EBUSY); +#endif + +#ifdef ECANCELED + ngx_js_errno_case(ECANCELED); +#endif + +#ifdef ECHILD + ngx_js_errno_case(ECHILD); +#endif + +#ifdef ECONNABORTED + ngx_js_errno_case(ECONNABORTED); +#endif + +#ifdef ECONNREFUSED + ngx_js_errno_case(ECONNREFUSED); +#endif + +#ifdef ECONNRESET + ngx_js_errno_case(ECONNRESET); +#endif + +#ifdef EDEADLK + ngx_js_errno_case(EDEADLK); +#endif + +#ifdef EDESTADDRREQ + ngx_js_errno_case(EDESTADDRREQ); +#endif + +#ifdef EDOM + ngx_js_errno_case(EDOM); +#endif + +#ifdef EDQUOT + ngx_js_errno_case(EDQUOT); +#endif + +#ifdef EEXIST + ngx_js_errno_case(EEXIST); +#endif + +#ifdef EFAULT + ngx_js_errno_case(EFAULT); +#endif + +#ifdef EFBIG + ngx_js_errno_case(EFBIG); +#endif + +#ifdef EHOSTUNREACH + ngx_js_errno_case(EHOSTUNREACH); +#endif + +#ifdef EIDRM + ngx_js_errno_case(EIDRM); +#endif + +#ifdef EILSEQ + ngx_js_errno_case(EILSEQ); +#endif + +#ifdef EINPROGRESS + ngx_js_errno_case(EINPROGRESS); +#endif + +#ifdef EINTR + ngx_js_errno_case(EINTR); +#endif + +#ifdef EINVAL + ngx_js_errno_case(EINVAL); +#endif + +#ifdef EIO + ngx_js_errno_case(EIO); +#endif + +#ifdef EISCONN + ngx_js_errno_case(EISCONN); +#endif + +#ifdef EISDIR + ngx_js_errno_case(EISDIR); +#endif + +#ifdef ELOOP + ngx_js_errno_case(ELOOP); +#endif + +#ifdef EMFILE + ngx_js_errno_case(EMFILE); +#endif + +#ifdef EMLINK + ngx_js_errno_case(EMLINK); +#endif + +#ifdef EMSGSIZE + ngx_js_errno_case(EMSGSIZE); +#endif + +#ifdef EMULTIHOP + ngx_js_errno_case(EMULTIHOP); +#endif + +#ifdef ENAMETOOLONG + ngx_js_errno_case(ENAMETOOLONG); +#endif + +#ifdef ENETDOWN + ngx_js_errno_case(ENETDOWN); +#endif + +#ifdef ENETRESET + ngx_js_errno_case(ENETRESET); +#endif + +#ifdef ENETUNREACH + ngx_js_errno_case(ENETUNREACH); +#endif + +#ifdef ENFILE + ngx_js_errno_case(ENFILE); +#endif + +#ifdef ENOBUFS + ngx_js_errno_case(ENOBUFS); +#endif + +#ifdef ENODATA + ngx_js_errno_case(ENODATA); +#endif + +#ifdef ENODEV + ngx_js_errno_case(ENODEV); +#endif + +#ifdef ENOENT + ngx_js_errno_case(ENOENT); +#endif + +#ifdef ENOEXEC + ngx_js_errno_case(ENOEXEC); +#endif + +#ifdef ENOLINK + ngx_js_errno_case(ENOLINK); +#endif + +#ifdef ENOLCK +#if ENOLINK != ENOLCK + ngx_js_errno_case(ENOLCK); +#endif +#endif + +#ifdef ENOMEM + ngx_js_errno_case(ENOMEM); +#endif + +#ifdef ENOMSG + ngx_js_errno_case(ENOMSG); +#endif + +#ifdef ENOPROTOOPT + ngx_js_errno_case(ENOPROTOOPT); +#endif + +#ifdef ENOSPC + ngx_js_errno_case(ENOSPC); +#endif + +#ifdef ENOSR + ngx_js_errno_case(ENOSR); +#endif + +#ifdef ENOSTR + ngx_js_errno_case(ENOSTR); +#endif + +#ifdef ENOSYS + ngx_js_errno_case(ENOSYS); +#endif + +#ifdef ENOTCONN + ngx_js_errno_case(ENOTCONN); +#endif + +#ifdef ENOTDIR + ngx_js_errno_case(ENOTDIR); +#endif + +#ifdef ENOTEMPTY +#if ENOTEMPTY != EEXIST + ngx_js_errno_case(ENOTEMPTY); +#endif +#endif + +#ifdef ENOTSOCK + ngx_js_errno_case(ENOTSOCK); +#endif + +#ifdef ENOTSUP + ngx_js_errno_case(ENOTSUP); +#else +#ifdef EOPNOTSUPP + ngx_js_errno_case(EOPNOTSUPP); +#endif +#endif + +#ifdef ENOTTY + ngx_js_errno_case(ENOTTY); +#endif + +#ifdef ENXIO + ngx_js_errno_case(ENXIO); +#endif + +#ifdef EOVERFLOW + ngx_js_errno_case(EOVERFLOW); +#endif + +#ifdef EPERM + ngx_js_errno_case(EPERM); +#endif + +#ifdef EPIPE + ngx_js_errno_case(EPIPE); +#endif + +#ifdef EPROTO + ngx_js_errno_case(EPROTO); +#endif + +#ifdef EPROTONOSUPPORT + ngx_js_errno_case(EPROTONOSUPPORT); +#endif + +#ifdef EPROTOTYPE + ngx_js_errno_case(EPROTOTYPE); +#endif + +#ifdef ERANGE + ngx_js_errno_case(ERANGE); +#endif + +#ifdef EROFS + ngx_js_errno_case(EROFS); +#endif + +#ifdef ESPIPE + ngx_js_errno_case(ESPIPE); +#endif + +#ifdef ESRCH + ngx_js_errno_case(ESRCH); +#endif + +#ifdef ESTALE + ngx_js_errno_case(ESTALE); +#endif + +#ifdef ETIME + ngx_js_errno_case(ETIME); +#endif + +#ifdef ETIMEDOUT + ngx_js_errno_case(ETIMEDOUT); +#endif + +#ifdef ETXTBSY + ngx_js_errno_case(ETXTBSY); +#endif + +#ifdef EXDEV + ngx_js_errno_case(EXDEV); +#endif + + default: + break; + } + + return "UNKNOWN CODE"; +} + + ngx_js_queue_t * ngx_js_queue_create(ngx_pool_t *pool, ngx_uint_t capacity) { diff --git a/nginx/ngx_js.h b/nginx/ngx_js.h index 8b6fbc85..8319dc85 100644 --- a/nginx/ngx_js.h +++ b/nginx/ngx_js.h @@ -412,6 +412,7 @@ njs_int_t ngx_js_ext_flags(njs_vm_t *vm, njs_object_prop_t *prop, ngx_int_t ngx_js_string(njs_vm_t *vm, njs_value_t *value, njs_str_t *str); ngx_int_t ngx_js_integer(njs_vm_t *vm, njs_value_t *value, ngx_int_t *n); +const char *ngx_js_errno_string(int errnum); ngx_js_queue_t *ngx_js_queue_create(ngx_pool_t *pool, ngx_uint_t capacity); ngx_int_t ngx_js_queue_push(ngx_js_queue_t *queue, void *item); diff --git a/src/njs.h b/src/njs.h index 466932d8..14a245af 100644 --- a/src/njs.h +++ b/src/njs.h @@ -72,6 +72,8 @@ extern const njs_value_t njs_value_undefined; njs_vm_error2(vm, 2, fmt, ##__VA_ARGS__) #define njs_vm_range_error(vm, fmt, ...) \ njs_vm_error2(vm, 3, fmt, ##__VA_ARGS__) +#define njs_vm_ref_error(vm, fmt, ...) \ + njs_vm_error2(vm, 4, fmt, ##__VA_ARGS__) #define njs_vm_syntax_error(vm, fmt, ...) \ njs_vm_error2(vm, 5, fmt, ##__VA_ARGS__) #define njs_vm_type_error(vm, fmt, ...) \ diff --git a/src/njs_module.c b/src/njs_module.c index 859d96a8..5d9c96ae 100644 --- a/src/njs_module.c +++ b/src/njs_module.c @@ -148,7 +148,7 @@ njs_module_require(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, module = njs_module_find(vm, &name, 0); if (njs_slow_path(module == NULL)) { - njs_error(vm, "Cannot find module \"%V\"", &name); + njs_error(vm, "Cannot load module \"%V\"", &name); return NJS_ERROR; } diff --git a/src/njs_parser.c b/src/njs_parser.c index 7eb6292e..1f16336f 100644 --- a/src/njs_parser.c +++ b/src/njs_parser.c @@ -8114,7 +8114,7 @@ njs_parser_module(njs_parser_t *parser, njs_str_t *name) vm = parser->vm; if (name->length == 0) { - njs_parser_syntax_error(parser, "Cannot find module \"%V\"", name); + njs_parser_ref_error(parser, "Cannot load module \"%V\"", name); return NULL; } @@ -8124,13 +8124,16 @@ njs_parser_module(njs_parser_t *parser, njs_str_t *name) } if (vm->module_loader == NULL) { - njs_parser_syntax_error(parser, "Cannot load module \"%V\"", name); + njs_parser_ref_error(parser, "Module loader callback is not provided"); return NULL; } module = vm->module_loader(vm, vm->module_loader_opaque, name); if (module == NULL) { - njs_parser_syntax_error(parser, "Cannot find module \"%V\"", name); + if (!njs_is_valid(&vm->exception)) { + njs_parser_ref_error(parser, "Cannot load module \"%V\"", name); + } + return NULL; } diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index c52753a7..c4dc6dde 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -18975,7 +18975,7 @@ static njs_unit_test_t njs_test[] = /* require(). */ { njs_str("require('unknown_module')"), - njs_str("Error: Cannot find module \"unknown_module\"") }, + njs_str("Error: Cannot load module \"unknown_module\"") }, { njs_str("require()"), njs_str("TypeError: missing path") }, @@ -19045,7 +19045,7 @@ static njs_unit_test_t njs_test[] = njs_str("SyntaxError: Unexpected token \"{\" in 1") }, { njs_str("import x from ''"), - njs_str("SyntaxError: Cannot find module \"\" in 1") }, + njs_str("ReferenceError: Cannot load module \"\" in 1") }, { njs_str("export"), njs_str("SyntaxError: Illegal export statement in 1") }, diff --git a/test/shell_test_njs.exp b/test/shell_test_njs.exp index 52ce4886..fac0fe3a 100644 --- a/test/shell_test_njs.exp +++ b/test/shell_test_njs.exp @@ -173,13 +173,13 @@ njs_test { # quiet mode njs_run {"-q" "test/js/import_chain.t.js"} \ - "SyntaxError: Cannot find module \"lib2.js\" in 7" + "ReferenceError: Cannot load module \"lib2.js\" in 7" # sandboxing njs_test { {"var fs = require('fs')\r\n" - "Error: Cannot find module \"fs\"\r\n"} + "Error: Cannot load module \"fs\"\r\n"} } "-s" njs_test { From noreply at nginx.com Tue Oct 22 02:05:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 22 Oct 2024 02:05:02 +0000 (UTC) Subject: [njs] Version 0.8.7. Message-ID: <20241022020502.73E524787F@pubserv1.nginx> details: https://github.com/nginx/njs/commit/ba6b9e157ef472dbcac17e32c55f3227daa3103c branches: master commit: ba6b9e157ef472dbcac17e32c55f3227daa3103c user: Dmitry Volyntsev date: Mon, 21 Oct 2024 18:04:48 -0700 description: Version 0.8.7. --- CHANGES | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/CHANGES b/CHANGES index 859ae077..746039ec 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,28 @@ +Changes with njs 0.8.7 22 Oct 2024 + + nginx modules: + + *) Bugfix: eliminated unnecessary VM creation. + Previously, njs consumed memory proportionally to the number of + nginx locations. The issue was introduced in 9b674412 (0.8.6). + + *) Improvement: added strict syntax validation for js_body_filter. + + *) Improvement: improved error messages for module loading + failures. + + Core: + + *) Feature: implemented fs.readlink() and friends. + + *) Improvement: implemented lazy stack symbolization. + + *) Bugfix: fixed heap-buffer-overflow in Buffer.prototype.indexOf(). + The issue was introduced in 5d15a8d6 (0.8.6). + + *) Bugfix: fixed Buffer.prototype.lastIndexOf() when `from` is + provided. + Changes with njs 0.8.6 02 Oct 2024 nginx modules: From noreply at nginx.com Tue Oct 22 02:06:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 22 Oct 2024 02:06:02 +0000 (UTC) Subject: [njs] Lightweight tag created: 0.8.7 Message-ID: <20241022020602.36C8847880@pubserv1.nginx> details: https://github.com/nginx/njs/releases/tag/0.8.7 branches: commit: ba6b9e157ef472dbcac17e32c55f3227daa3103c user: Dmitry Volyntsev date: Mon Oct 21 18:04:48 2024 -0700 description: Version 0.8.7.