diff --git a/fuzzing/nxt_basic_fuzz.c b/fuzzing/nxt_basic_fuzz.c index df3a1b6a8..5f71a909c 100644 --- a/fuzzing/nxt_basic_fuzz.c +++ b/fuzzing/nxt_basic_fuzz.c @@ -3,9 +3,15 @@ */ #include +#include +#include +#include +/* DO NOT TRY THIS AT HOME! */ +#include -#define KMININPUTLENGTH 2 + +#define KMININPUTLENGTH 4 #define KMAXINPUTLENGTH 128 @@ -13,9 +19,17 @@ extern int LLVMFuzzerInitialize(int *argc, char ***argv); extern int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); void nxt_base64_fuzz(const u_char *data, size_t size); +void nxt_djb_hash_fuzz(const u_char *data, size_t size); +void nxt_murmur_hash2_fuzz(const u_char *data, size_t size); +void nxt_parse_fuzz(const u_char *data, size_t size); +void nxt_sha1_fuzz(const u_char *data, size_t size); +void nxt_sha1_update_fuzz(const u_char *data, size_t size); void nxt_term_fuzz(const u_char *data, size_t size); void nxt_time_fuzz(const u_char *data, size_t size); +void nxt_uri_fuzz(const u_char *data, size_t size); void nxt_utf8_fuzz(const u_char *data, size_t size); +void nxt_websocket_base64_fuzz(const u_char *data, size_t size); +void nxt_websocket_frame_fuzz(const u_char *data, size_t size); extern char **environ; @@ -40,9 +54,17 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) } nxt_base64_fuzz(data, size); + nxt_djb_hash_fuzz(data, size); + nxt_murmur_hash2_fuzz(data, size); + nxt_parse_fuzz(data, size); + nxt_sha1_fuzz(data, size); + nxt_sha1_update_fuzz(data, size); nxt_term_fuzz(data, size); nxt_time_fuzz(data, size); + nxt_uri_fuzz(data, size); nxt_utf8_fuzz(data, size); + nxt_websocket_base64_fuzz(data, size); + nxt_websocket_frame_fuzz(data, size); return 0; } @@ -66,6 +88,64 @@ nxt_base64_fuzz(const u_char *data, size_t size) } +void +nxt_djb_hash_fuzz(const u_char *data, size_t size) +{ + nxt_djb_hash(data, size); + nxt_djb_hash_lowcase(data, size); +} + + +void +nxt_murmur_hash2_fuzz(const u_char *data, size_t size) +{ + nxt_murmur_hash2(data, size); + nxt_murmur_hash2_uint32(data); +} + + +void +nxt_parse_fuzz(const u_char *data, size_t size) +{ + nxt_str_t input; + + input.start = (u_char *)data; + input.length = size; + + nxt_int_parse(data, size); + nxt_size_t_parse(data, size); + nxt_size_parse(data, size); + nxt_off_t_parse(data, size); + nxt_str_int_parse(&input); + nxt_number_parse(&data, data + size); +} + + +void +nxt_sha1_fuzz(const u_char *data, size_t size) +{ + u_char bin_accept[20]; + nxt_sha1_t ctx; + + nxt_sha1_init(&ctx); + nxt_sha1_update(&ctx, data, size); + nxt_sha1_final(bin_accept, &ctx); +} + + +void +nxt_sha1_update_fuzz(const u_char *data, size_t size) +{ + u_char bin_accept[20]; + nxt_sha1_t ctx; + + nxt_sha1_init(&ctx); + nxt_sha1_update(&ctx, data, size); + nxt_sha1_update(&ctx, data, size); + nxt_sha1_final(bin_accept, &ctx); +} + + void nxt_term_fuzz(const u_char *data, size_t size) { @@ -81,6 +161,27 @@ nxt_time_fuzz(const u_char *data, size_t size) } +void +nxt_uri_fuzz(const u_char *data, size_t size) +{ + u_char *dst; + + dst = nxt_zalloc(size * 3); + if (dst == NULL) { + return; + } + + nxt_decode_uri(dst, (u_char *)data, size); + nxt_decode_uri_plus(dst, (u_char *)data, size); + + nxt_memzero(dst, size * 3); + nxt_encode_uri(NULL, (u_char *)data, size); + nxt_encode_uri(dst, (u_char *)data, size); + + nxt_free(dst); +} + + void nxt_utf8_fuzz(const u_char *data, size_t size) { @@ -88,4 +189,48 @@ nxt_utf8_fuzz(const u_char *data, size_t size) in = data; nxt_utf8_decode(&in, data + size); + + nxt_utf8_casecmp((const u_char *)"ABC АБВ ΑΒΓ", + data, + nxt_length("ABC АБВ ΑΒΓ"), + size); +} + + +void +nxt_websocket_base64_fuzz(const u_char *data, size_t size) +{ + u_char *out; + + out = nxt_zalloc(size * 2); + if (out == NULL) { + return; + } + + nxt_websocket_base64_encode(out, data, size); + + nxt_free(out); +} + + +void +nxt_websocket_frame_fuzz(const u_char *data, size_t size) +{ + u_char *input; + + /* + * Resolve overwrites-const-input by using a copy of the data. + */ + input = nxt_malloc(size); + if (input == NULL) { + return; + } + + nxt_memcpy(input, data, size); + + nxt_websocket_frame_init(input, 0); + nxt_websocket_frame_header_size(input); + nxt_websocket_frame_payload_len(input); + + nxt_free(input); } diff --git a/fuzzing/nxt_http_controller_fuzz.c b/fuzzing/nxt_http_controller_fuzz.c index eac54d7b0..25527ae14 100644 --- a/fuzzing/nxt_http_controller_fuzz.c +++ b/fuzzing/nxt_http_controller_fuzz.c @@ -43,8 +43,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { nxt_mp_t *mp; + nxt_int_t rc; nxt_buf_mem_t buf; - nxt_controller_request_t *r_controller; + nxt_controller_request_t *req; nxt_http_request_parse_t rp; if (size < KMININPUTLENGTH || size > KMAXINPUTLENGTH) { @@ -56,8 +57,13 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) return 0; } - nxt_memzero(&rp, sizeof(nxt_http_request_parse_t)); - if (nxt_http_parse_request_init(&rp, mp) != NXT_OK) { + req = nxt_mp_zget(mp, sizeof(nxt_controller_request_t)); + if (req == NULL) { + goto failed; + } + + req->conn = nxt_mp_zget(mp, sizeof(nxt_conn_t)); + if (req->conn == NULL) { goto failed; } @@ -66,26 +72,23 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) buf.pos = buf.start; buf.free = buf.end; - if (nxt_http_parse_request(&rp, &buf) != NXT_DONE) { - goto failed; - } + nxt_main_log.level = NXT_LOG_ALERT; + req->conn->log = nxt_main_log; - r_controller = nxt_mp_zget(mp, sizeof(nxt_controller_request_t)); + nxt_memzero(&rp, sizeof(nxt_http_request_parse_t)); - if (r_controller == NULL) { + rc = nxt_http_parse_request_init(&rp, mp); + if (rc != NXT_OK) { goto failed; } - r_controller->conn = nxt_mp_zget(mp, sizeof(nxt_conn_t)); - if (r_controller->conn == NULL) { + rc = nxt_http_parse_request(&rp, &buf); + if (rc != NXT_DONE) { goto failed; } - nxt_main_log.level = NXT_LOG_ALERT; - r_controller->conn->log = nxt_main_log; - nxt_http_fields_process(rp.fields, &nxt_controller_fields_hash, - r_controller); + req); failed: diff --git a/fuzzing/nxt_http_h1p_fuzz.c b/fuzzing/nxt_http_h1p_fuzz.c index a170463a0..b8893ad6f 100644 --- a/fuzzing/nxt_http_h1p_fuzz.c +++ b/fuzzing/nxt_http_h1p_fuzz.c @@ -42,8 +42,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { nxt_mp_t *mp; + nxt_int_t rc; nxt_buf_mem_t buf; - nxt_http_request_t *r_h1p; + nxt_http_request_t *req; nxt_http_request_parse_t rp; if (size < KMININPUTLENGTH || size > KMAXINPUTLENGTH) { @@ -55,8 +56,23 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) return 0; } - nxt_memzero(&rp, sizeof(nxt_http_request_parse_t)); - if (nxt_http_parse_request_init(&rp, mp) != NXT_OK) { + req = nxt_mp_zget(mp, sizeof(nxt_http_request_t)); + if (req == NULL) { + goto failed; + } + + req->proto.h1 = nxt_mp_zget(mp, sizeof(nxt_h1proto_t)); + if (req->proto.h1 == NULL) { + goto failed; + } + + req->conf = nxt_mp_zget(mp, sizeof(nxt_socket_conf_joint_t)); + if (req->conf == NULL) { + goto failed; + } + + req->conf->socket_conf = nxt_mp_zget(mp, sizeof(nxt_socket_conf_t)); + if (req->conf->socket_conf == NULL) { goto failed; } @@ -65,19 +81,22 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) buf.pos = buf.start; buf.free = buf.end; - if (nxt_http_parse_request(&rp, &buf) != NXT_DONE) { - goto failed; - } + req->mem_pool = mp; + req->conf->socket_conf->max_body_size = 8 * 1024 * 1024; - r_h1p = nxt_mp_zget(mp, sizeof(nxt_http_request_t)); + nxt_memzero(&rp, sizeof(nxt_http_request_parse_t)); - if (r_h1p == NULL) { + rc = nxt_http_parse_request_init(&rp, mp); + if (rc != NXT_OK) { goto failed; } - r_h1p->mem_pool = mp; + rc = nxt_http_parse_request(&rp, &buf); + if (rc != NXT_DONE) { + goto failed; + } - nxt_http_fields_process(rp.fields, &nxt_h1p_fields_hash, r_h1p); + nxt_http_fields_process(rp.fields, &nxt_h1p_fields_hash, req); failed: diff --git a/fuzzing/nxt_http_h1p_peer_fuzz.c b/fuzzing/nxt_http_h1p_peer_fuzz.c index 7b7222481..54876658c 100644 --- a/fuzzing/nxt_http_h1p_peer_fuzz.c +++ b/fuzzing/nxt_http_h1p_peer_fuzz.c @@ -43,8 +43,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { nxt_mp_t *mp; + nxt_int_t rc; nxt_buf_mem_t buf; - nxt_http_request_t *r_h1p_peer; + nxt_http_request_t *req; nxt_http_request_parse_t rp; if (size < KMININPUTLENGTH || size > KMAXINPUTLENGTH) { @@ -56,8 +57,8 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) return 0; } - nxt_memzero(&rp, sizeof(nxt_http_request_parse_t)); - if (nxt_http_parse_request_init(&rp, mp) != NXT_OK) { + req = nxt_mp_zget(mp, sizeof(nxt_http_request_t)); + if (req == NULL) { goto failed; } @@ -66,17 +67,19 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) buf.pos = buf.start; buf.free = buf.end; - if (nxt_http_parse_request(&rp, &buf) != NXT_DONE) { + nxt_memzero(&rp, sizeof(nxt_http_request_parse_t)); + + rc = nxt_http_parse_request_init(&rp, mp); + if (rc != NXT_OK) { goto failed; } - r_h1p_peer = nxt_mp_zget(mp, sizeof(nxt_http_request_t)); - - if (r_h1p_peer == NULL) { + rc = nxt_http_parse_request(&rp, &buf); + if (rc != NXT_DONE) { goto failed; } - nxt_http_fields_process(rp.fields, &nxt_h1p_peer_fields_hash, r_h1p_peer); + nxt_http_fields_process(rp.fields, &nxt_h1p_peer_fields_hash, req); failed: diff --git a/fuzzing/nxt_json_fuzz.c b/fuzzing/nxt_json_fuzz.c index cfeb395da..fa2229887 100644 --- a/fuzzing/nxt_json_fuzz.c +++ b/fuzzing/nxt_json_fuzz.c @@ -31,12 +31,13 @@ LLVMFuzzerInitialize(int *argc, char ***argv) int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { - nxt_mp_t *mp; - nxt_str_t input; - nxt_thread_t *thr; - nxt_runtime_t *rt; - nxt_conf_value_t *conf; - nxt_conf_validation_t vldt; + nxt_mp_t *mp; + nxt_str_t input; + nxt_thread_t *thr; + nxt_runtime_t *rt; + nxt_conf_value_t *conf; + nxt_conf_validation_t vldt; + nxt_conf_json_pretty_t pretty; if (size < KMININPUTLENGTH || size > KMAXINPUTLENGTH) { return 0; @@ -54,18 +55,27 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) goto failed; } - thr->runtime = rt; - rt->mem_pool = mp; + rt->languages = nxt_array_create(mp, 1, sizeof(nxt_app_lang_module_t)); + if (rt->languages == NULL) { + goto failed; + } input.start = (u_char *)data; input.length = size; + thr->runtime = rt; + rt->mem_pool = mp; + + nxt_memzero(&pretty, sizeof(nxt_conf_json_pretty_t)); + nxt_memzero(&vldt, sizeof(nxt_conf_validation_t)); + conf = nxt_conf_json_parse_str(mp, &input); if (conf == NULL) { goto failed; } - nxt_memzero(&vldt, sizeof(nxt_conf_validation_t)); + nxt_conf_json_length(conf, NULL); + nxt_conf_json_length(conf, &pretty); vldt.pool = nxt_mp_create(1024, 128, 256, 32); if (vldt.pool == NULL) { @@ -76,13 +86,7 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) vldt.conf_pool = mp; vldt.ver = NXT_VERNUM; - rt->languages = nxt_array_create(mp, 1, sizeof(nxt_app_lang_module_t)); - if (rt->languages == NULL) { - goto failed; - } - nxt_conf_validate(&vldt); - nxt_mp_destroy(vldt.pool); failed: diff --git a/tools/unitctl/README.md b/tools/unitctl/README.md index e6fca4772..9f7e010b2 100644 --- a/tools/unitctl/README.md +++ b/tools/unitctl/README.md @@ -112,7 +112,7 @@ The new containers will then be shown in a call to $ unitctl instances new /tmp/2 $(pwd) 'unit:wasm' Pulling and starting a container from unit:wasm Will mount /tmp/2 to /var/run for socket access -Will READ ONLY mount /home/ava/repositories/nginx/unit/tools/unitctl to /www for application access +Will mount /home/user/repositories/nginx/unit/tools/unitctl to /www for application access Note: Container will be on host network ``` @@ -131,12 +131,17 @@ To the subcommand `unitctl instances new` the user must provide three arguments: For example: `127.0.0.1:7171`. 2. **A path to an application:** In the example, `$(pwd)` is provided. The Unit container will mount - this READ ONLY to `/www/`. This will allow the user to configure - their Unit container to expose an application stored on the host. + this to `/www/`. This will allow the user to configure their + Unit container to expose an application stored on the host. 3. **An image tag:** In the example, `unit:wasm` is used. This will be the image that unitctl will deploy. Custom repos and images can be deployed in this manner. +In addition to the above arguments, the user may add the `-r` flag. This flag will +set the Docker volume mount for the application directory to be read only. Do note +that this flag will break compatibility with WordPress, and other applications +which store state on the file system. + After deployment the user will have one Unit container running on the host network. ### Lists active applications and provides means to restart them diff --git a/tools/unitctl/unit-client-rs/src/unitd_docker.rs b/tools/unitctl/unit-client-rs/src/unitd_docker.rs index 0d318096c..2b9e0c7d9 100644 --- a/tools/unitctl/unit-client-rs/src/unitd_docker.rs +++ b/tools/unitctl/unit-client-rs/src/unitd_docker.rs @@ -249,6 +249,7 @@ impl UnitdContainer { pub async fn deploy_new_container( socket: ControlSocket, application: &String, + application_read_only: bool, image: &String, ) -> Result, UnitClientError> { match Docker::connect_with_local_defaults() { @@ -269,7 +270,7 @@ pub async fn deploy_new_container( typ: Some(MountTypeEnum::BIND), source: Some(application.clone()), target: Some("/www".to_string()), - read_only: Some(true), + read_only: Some(application_read_only), ..Default::default() }); diff --git a/tools/unitctl/unitctl/src/cmd/instances.rs b/tools/unitctl/unitctl/src/cmd/instances.rs index e532a1517..92e092014 100644 --- a/tools/unitctl/unitctl/src/cmd/instances.rs +++ b/tools/unitctl/unitctl/src/cmd/instances.rs @@ -13,6 +13,7 @@ pub(crate) async fn cmd(args: InstanceArgs) -> Result<(), UnitctlError> { InstanceCommands::New { ref socket, ref application, + ref application_read_only, ref image, } => { // validation for application dir @@ -95,7 +96,12 @@ pub(crate) async fn cmd(args: InstanceArgs) -> Result<(), UnitctlError> { // reflect changes to user // print this to STDERR to avoid polluting deserialized data output eprintln!("> Pulling and starting a container from {}", image); - eprintln!("> Will READ ONLY mount {} to /www for application access", application); + eprintln!("> Will mount {} to /www for application access", application); + + if *application_read_only { + eprintln!("> Application mount will be read only"); + } + eprintln!("> Container will be on host network"); match addr.as_ref().unwrap() { ControlSocket::UnixLocalSocket(path) => eprintln!( @@ -113,7 +119,7 @@ pub(crate) async fn cmd(args: InstanceArgs) -> Result<(), UnitctlError> { } // do the actual deployment - deploy_new_container(addr.unwrap(), application, image) + deploy_new_container(addr.unwrap(), application, *application_read_only, image) .await .map_or_else( |e| Err(UnitctlError::UnitClientError { source: e }), diff --git a/tools/unitctl/unitctl/src/unitctl.rs b/tools/unitctl/unitctl/src/unitctl.rs index a36f006ce..8db71b8f9 100644 --- a/tools/unitctl/unitctl/src/unitctl.rs +++ b/tools/unitctl/unitctl/src/unitctl.rs @@ -51,7 +51,7 @@ pub(crate) enum Commands { short = 't', long = "output-format", default_value = "json-pretty", - help = "Output format: yaml, json, json-pretty (default)" + help = "Output format of the result" )] output_format: OutputFormat, }, @@ -68,7 +68,7 @@ pub(crate) enum Commands { short = 't', long = "output-format", default_value = "json-pretty", - help = "Output format: yaml, json, json-pretty (default)" + help = "Output format of the result" )] output_format: OutputFormat, #[arg( @@ -98,7 +98,7 @@ pub(crate) enum Commands { short = 't', long = "output-format", default_value = "json-pretty", - help = "Output format: yaml, json, json-pretty (default)" + help = "Output format of the result" )] output_format: OutputFormat, }, @@ -110,7 +110,7 @@ pub(crate) enum Commands { short = 't', long = "output-format", default_value = "json-pretty", - help = "Output format: yaml, json, json-pretty (default)" + help = "Output format of the result" )] output_format: OutputFormat, }, @@ -119,12 +119,8 @@ pub(crate) enum Commands { #[command(about = "Export the current configuration of UNIT")] Export { - #[arg( - required = true, - short = 'f', - help = "tarball filename to save configuration to" - )] - filename: String + #[arg(required = true, short = 'f', help = "tarball filename to save configuration to")] + filename: String, }, } @@ -135,8 +131,8 @@ pub struct InstanceArgs { global = true, short = 't', long = "output-format", - default_value = "text", - help = "Output format: text, yaml, json, json-pretty (default)" + default_value = "json-pretty", + help = "Output format of the result" )] pub output_format: OutputFormat, @@ -155,6 +151,9 @@ pub enum InstanceCommands { #[arg(required = true, help = "Path to mount application into container")] application: String, + #[arg(help = "Mount application directory as read only", short = 'r', long = "read-only")] + application_read_only: bool, + #[arg( help = "Unitd Image to deploy", default_value = env!("CARGO_PKG_VERSION"), @@ -170,8 +169,8 @@ pub struct ApplicationArgs { global = true, short = 't', long = "output-format", - default_value = "text", - help = "Output format: text, yaml, json, json-pretty (default)" + default_value = "json-pretty", + help = "Output format of the result" )] pub output_format: OutputFormat,