Opened 4 weeks ago

#2213 new defect

The get_handler of ngx_http_variable_t is overwritten by ngx_http_regex_compile if existing

Reported by: sansanvang@… Owned by:
Priority: major Milestone:
Component: nginx-module Version: 1.19.x
Keywords: get_handler, ngx_http_variable_t Cc:
uname -a: Linux emsp 3.10.0-1127.19.1.el7.x86_64 #1 SMP Tue Aug 25 17:23:54 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
nginx -V: nginx version: nginx/1.19.3 (emsp-1.0.1)
built by gcc 8.3.1 20191121 (Red Hat 8.3.1-5) (GCC)
built with OpenSSL 1.1.1g FIPS 21 Apr 2020
TLS SNI support enabled
configure arguments: --builddir=../objs --prefix=/usr/local/nginx --user=nginx --group=nginx --with-pcre --with-http_ssl_module --with-http_stub_status_module --with-http_gzip_static_module --with-compat --add-dynamic-module=../nginx-emsp-module --with-threads --with-debug --with-cc-opt='-O0 -DNGX_BUILD="emsp-1.0.1" -DNGX_API= -Wno-error=unused-function -Wno-missing-field-initializers'

Description

I'm developing a dynamical NGINX module. I added a variable at preconfiguration stage and print its get_handler at postconfiguration stage. Because I added a named capture group in a regex location directive of nginx.conf, i.e. the variable would be set/defined (set access) at configuration stage, I used the NGX_HTTP_VAR_CHANGEABLE flag. However, I found this variable was empty when it's used in a subrequest location block. Then I checked its get_handler at postconfiguration stage and I found it was overrided.

So I think we should check its value of get_handler before setting it.

The key C++ code snippet (some omitted for brevity),

#define NGX_HTTP_VAR_sp_resid "sp_resid"
static const ngx_str_t sp_resid_name = ngx_string(NGX_HTTP_VAR_sp_resid)
#define NGX_HTTP_EM_VAR_NAME(name) const_cast<ngx_str_t*>(&::name##_name)

/* The module context. */
static ngx_http_module_t ngx_http_em_module_ctx = {
	ngx_http_em_preconfiguration, /* preconfiguration */
	ngx_http_em_postconfiguration,	/* postconfiguration */

	ngx_http_em_create_main_conf,	/* create main configuration */
	ngx_http_em_init_main_conf,		/* init main configuration */

	NULL, /* create server configuration */
	NULL, /* merge server configuration */

	ngx_http_em_create_loc_conf,	/* create location-specific configuration */
	ngx_http_em_merge_loc_conf,	/* merge location configuration */
};


static ngx_int_t ngx_http_sp_resid_variable(ngx_http_request_t *req, ngx_http_variable_value_t *vv, uintptr_t data) {
	const auto ctx = ngx_http_em_get_module_ctx(req->main);
	logdf("ctx@%p, req=%.*s?%.*s, spResId=%.*s", ctx, ARGS_NGX_STR(req->uri), ARGS_NGX_STR(req->args), ARGS_NGX_STR(ctx->spResId));
	if (ctx == NULL) {
		vv->not_found = 1;
		return NGX_OK;
	}
	vv->valid = 1;
	vv->no_cacheable = 0;
	vv->not_found = 0;
	vv->data = ctx->spResId.data;
	vv->len = ctx->spResId.len;
	return NGX_OK;
}

ngx_int_t ngx_http_em_preconfiguration(ngx_conf_t *cf) {
	ngx_http_variable_t  *var;
	var = ngx_http_add_variable(cf, NGX_HTTP_EM_VAR_NAME(sp_resid), NGX_HTTP_VAR_CHANGEABLE | NGX_HTTP_VAR_NOCACHEABLE);
	if (var == NULL) {
		return NGX_ERROR;
	}
	var->get_handler = ngx_http_sp_resid_variable;
	return NGX_OK;
}

ngx_int_t ngx_http_em_postconfiguration(ngx_conf_t *cf) {
	// Restore v->get_handler = ngx_http_variable_not_found set
	// by ngx_http_regex_t *ngx_http_regex_compile(ngx_conf_t *cf, ngx_regex_compile_t *rc)
	// in nginx\nginx\src\http\ngx_http_variables.c
	auto vars_keys = cmcf->variables_keys->keys;
	auto keys = static_cast<ngx_hash_key_t*>(vars_keys.elts);
	for (auto idx = vars_keys.nelts; idx > 0;) {
		const auto& var_entry = keys[--idx]; // ngx_strncasecmp
		if (sizeof(NGX_HTTP_VAR_sp_resid) - 1 == var_entry.key.len &&
			0 == ngx_strncasecmp(PUChar(NGX_HTTP_VAR_sp_resid), var_entry.key.data, sizeof(NGX_HTTP_VAR_sp_resid) - 1)) {
			const auto var = static_cast<ngx_http_variable_t*>(var_entry.value);
			logdf("var->get_handler=%p, ngx_http_sp_resid_variable=%p", var->get_handler, ngx_http_sp_resid_variable);
			var->get_handler = ngx_http_sp_resid_variable; // workaround
		}
	}
}

nginx.conf

location / {
	proxy_pass $scheme://$host;
}

location ~ "^/_api/(?<sp_resid>[[:xdigit:]]{8}(?:-[[:xdigit:]]{4}){3}-[[:xdigit:]]{12})/driveItem$" {
	proxy_pass $scheme://$host;
}

location = /GetList {
	internal;
	subrequest_output_buffer_size 128k;
	proxy_set_header Content-Length "";
	proxy_set_header Accept-Encoding "";
	proxy_set_header Accept "application/json;odata=nometadata";
	proxy_pass $scheme://$host/_api/web/GetList(@a1)?@a1='$sp_resid'&%24expand=RootFolder;
}

Change History (0)

Note: See TracTickets for help on using tickets.