On 1/3/20 1:46 AM, Petr Štetiar wrote:
> Fixes following deficiencies:
> 
>  * unhandled read() errors
>  * everything bundled in one long function, which is hard to follow and
>    reason about
>  * JSON parser errors are being ignored, anything else then
>    json_tokener_continue is fatal error
>  * JSON parser errors are being output to stderr, thus invisible via SSH
>  * validate_firmware_image_call can fail at a lot of places, but we just
>    get one generic "Firmware image couldn't be validated" so it's hard
>    to debug
> 
> Cc: Rafał Miłecki <ra...@milecki.pl>
> Signed-off-by: Petr Štetiar <yn...@true.cz>
> ---
>  system.c | 170 ++++++++++++++++++++++++++++++++++++++++---------------
>  1 file changed, 123 insertions(+), 47 deletions(-)
> 
> diff --git a/system.c b/system.c
> index 5cd88e0d8227..f0198a5b20b8 100644
> --- a/system.c
> +++ b/system.c
> @@ -37,6 +37,12 @@ static struct blob_buf b;
>  static int notify;
>  static struct ubus_context *_ctx;
>  
> +enum vjson_state {
> +     VJSON_ERROR,
> +     VJSON_CONTINUE,
> +     VJSON_SUCCESS,
> +};
> +
>  static int system_board(struct ubus_context *ctx, struct ubus_object *obj,
>                   struct ubus_request_data *req, const char *method,
>                   struct blob_attr *msg)
> @@ -413,30 +419,127 @@ static int proc_signal(struct ubus_context *ctx, 
> struct ubus_object *obj,
>       return 0;
>  }
>  
> +static enum vjson_state vjson_error(char **b, const char *fmt, ...)

Please annotate the function with:
__attribute__ ((format (printf, 2, 3)));

> +{
> +     static char buf[256] = { 0 };
> +     const char *pfx = "Firmware image couldn't be validated: ";
> +     va_list va;
> +     int r;
> +
> +     r = snprintf(buf, sizeof(buf), "%s", pfx);
> +     if (r < 0) {
> +             *b = "vjson_error() snprintf failed";
> +             return VJSON_ERROR;
> +     }
> +
> +     va_start(va, fmt);
> +     r = vsnprintf(buf+r, sizeof(buf)-r, fmt, va);
Please check here for truncation:
rv = vsnprintf(buf+r, sizeof(buf)-r, fmt, va);
if (rv < 0 || rv >=  sizeof(buf)-r ) {


> +     if (r < 0) {
> +             *b = "vjson_error() vsnprintf failed";
> +             return VJSON_ERROR;
> +     }
> +     va_end(va);
> +
> +     *b = buf;
> +     return VJSON_ERROR;
> +}
> +
> +static enum vjson_state vjson_parse_token(json_tokener *tok, char *buf, 
> ssize_t len, char **err)
> +{
> +     json_object *jsobj = NULL;
> +
> +     jsobj = json_tokener_parse_ex(tok, buf, len);
> +     if (json_tokener_get_error(tok) == json_tokener_continue)
> +             return VJSON_CONTINUE;
> +
> +     if (json_tokener_get_error(tok) == json_tokener_success) {
> +             if (json_object_get_type(jsobj) != json_type_object) {
> +                     json_object_put(jsobj);
> +                     return vjson_error(err, "result is not an JSON object");
> +             }
> +
> +             blobmsg_add_object(&b, jsobj);
> +             json_object_put(jsobj);
> +             return VJSON_SUCCESS;
> +     }
> +
> +     return vjson_error(err, "failed to parse JSON: %s (%d)",
> +                        json_tokener_error_desc(json_tokener_get_error(tok)),
> +                        json_tokener_get_error(tok));

Why don't you free it here too json_object_put()?

> +}
> +
> +static enum vjson_state vjson_parse(int fd, char **err)
> +{
> +     enum vjson_state r = VJSON_ERROR;
> +     size_t read_count = 0;
> +     char buf[64] = { 0 };
> +     json_tokener *tok;
> +     ssize_t len;
> +     int _errno;
> +
> +     tok = json_tokener_new();
> +     if (!tok)
> +             return vjson_error(err, "json_tokener_new() failed");
> +
> +     vjson_error(err, "incomplete JSON input");
> +
> +     while ((len = read(fd, buf, sizeof(buf)))) {
> +             if (len < 0 && errno == EINTR)
> +                     continue;
> +
> +             if (len < 0) {
> +                     _errno = errno;
> +                     json_tokener_free(tok);
> +                     return vjson_error(err, "read() failed: %s (%d)",
> +                                        strerror(_errno), _errno);
> +             }
> +
> +             read_count += len;
> +             r = vjson_parse_token(tok, buf, len, err);
> +             if (r != VJSON_CONTINUE)
> +                     break;
> +
> +             memset(buf, 0, sizeof(buf));
> +     }
> +
> +     if (read_count == 0)
> +             vjson_error(err, "no JSON input");
> +
> +     json_tokener_free(tok);
> +     return r;
> +}
> +
>  /**
>   * validate_firmware_image_call - perform validation & store result in 
> global b
>   *
>   * @file: firmware image path
>   */
> -static int validate_firmware_image_call(const char *file)
> +static enum vjson_state validate_firmware_image_call(const char *file, char 
> **err)
>  {
>       const char *path = "/usr/libexec/validate_firmware_image";
> -     json_object *jsobj = NULL;
> -     json_tokener *tok;
> -     char buf[64];
> -     ssize_t len;
> +     enum vjson_state ret = VJSON_ERROR;
> +     int _errno;
>       int fds[2];
> -     int err;
>       int fd;
>  
> -     if (pipe(fds))
> -             return -errno;
> +     blob_buf_init(&b, 0);
> +     vjson_error(err, "unhandled error");
In which case is this returned, aren't there specific error handlers in
call cases now and vjson_parse() would overwrite it again?

> +
> +     if (pipe(fds)) {
> +             _errno = errno;
> +             return vjson_error(err, "pipe() failed: %s (%d)",
> +                                strerror(_errno), _errno);
> +     }
>  
>       switch (fork()) {
>       case -1:
> +             _errno = errno;
> +
>               close(fds[0]);
>               close(fds[1]);
> -             return -errno;
> +
> +             return vjson_error(err, "fork() failed: %s (%d)",
> +                                strerror(_errno), _errno);
>       case 0:
>               /* Set stdin & stderr to /dev/null */
>               fd = open("/dev/null", O_RDWR);
> @@ -458,43 +561,10 @@ static int validate_firmware_image_call(const char 
> *file)
>       /* Parent process */
>       close(fds[1]);
>  
> -     tok = json_tokener_new();
> -     if (!tok) {
> -             close(fds[0]);
> -             return -ENOMEM;
> -     }
> -
> -     blob_buf_init(&b, 0);
> -     while ((len = read(fds[0], buf, sizeof(buf)))) {
> -             if (len < 0 && errno == EINTR)
> -                     continue;
> -
> -             jsobj = json_tokener_parse_ex(tok, buf, len);
> -
> -             if (json_tokener_get_error(tok) == json_tokener_success)
> -                     break;
> -             else if (json_tokener_get_error(tok) == json_tokener_continue)
> -                     continue;
> -             else
> -                     fprintf(stderr, "Failed to parse JSON: %d\n",
> -                             json_tokener_get_error(tok));
> -     }
> -
> +     ret = vjson_parse(fds[0], err);
>       close(fds[0]);
>  
> -     err = -ENOENT;
> -     if (jsobj) {
> -             if (json_object_get_type(jsobj) == json_type_object) {
> -                     blobmsg_add_object(&b, jsobj);
> -                     err = 0;
> -             }
> -
> -             json_object_put(jsobj);
> -     }
> -
> -     json_tokener_free(tok);
> -
> -     return err;
> +     return ret;
>  }
>  
>  enum {
> @@ -512,6 +582,8 @@ static int validate_firmware_image(struct ubus_context 
> *ctx,
>                                  const char *method, struct blob_attr *msg)
>  {
>       struct blob_attr *tb[__VALIDATE_FIRMWARE_IMAGE_MAX];
> +     enum vjson_state ret = VJSON_ERROR;
> +     char *err;
>  
>       if (!msg)
>               return UBUS_STATUS_INVALID_ARGUMENT;
> @@ -520,7 +592,8 @@ static int validate_firmware_image(struct ubus_context 
> *ctx,
>       if (!tb[VALIDATE_FIRMWARE_IMAGE_PATH])
>               return UBUS_STATUS_INVALID_ARGUMENT;
>  
> -     if 
> (validate_firmware_image_call(blobmsg_get_string(tb[VALIDATE_FIRMWARE_IMAGE_PATH])))
> +     ret = 
> validate_firmware_image_call(blobmsg_get_string(tb[VALIDATE_FIRMWARE_IMAGE_PATH]),
>  &err);
> +     if (ret != VJSON_SUCCESS)
>               return UBUS_STATUS_UNKNOWN_ERROR;
>  
>       ubus_send_reply(ctx, req, b.head);
> @@ -580,6 +653,8 @@ static int sysupgrade(struct ubus_context *ctx, struct 
> ubus_object *obj,
>       struct blob_attr *validation[__VALIDATION_MAX];
>       struct blob_attr *tb[__SYSUPGRADE_MAX];
>       bool valid, forceable, allow_backup;
> +     enum vjson_state ret = VJSON_ERROR;
> +     char *err;
>  
>       if (!msg)
>               return UBUS_STATUS_INVALID_ARGUMENT;
> @@ -588,8 +663,9 @@ static int sysupgrade(struct ubus_context *ctx, struct 
> ubus_object *obj,
>       if (!tb[SYSUPGRADE_PATH] || !tb[SYSUPGRADE_PREFIX])
>               return UBUS_STATUS_INVALID_ARGUMENT;
>  
> -     if 
> (validate_firmware_image_call(blobmsg_get_string(tb[SYSUPGRADE_PATH]))) {
> -             sysupgrade_error(ctx, req, "Firmware image couldn't be 
> validated");
> +     ret = 
> validate_firmware_image_call(blobmsg_get_string(tb[SYSUPGRADE_PATH]), &err);
> +     if (ret != VJSON_SUCCESS) {
> +             sysupgrade_error(ctx, req, err);
>               return UBUS_STATUS_UNKNOWN_ERROR;
>       }
>  
> 
> _______________________________________________
> openwrt-devel mailing list
> openwrt-devel@lists.openwrt.org
> https://lists.openwrt.org/mailman/listinfo/openwrt-devel
> 


Attachment: signature.asc
Description: OpenPGP digital signature

_______________________________________________
openwrt-devel mailing list
openwrt-devel@lists.openwrt.org
https://lists.openwrt.org/mailman/listinfo/openwrt-devel

Reply via email to