[Libguestfs] [PATCH] daemon: fix memory allocation and leaks in OCaml stubs

Richard W.M. Jones rjones at redhat.com
Thu May 3 16:48:19 UTC 2018


On Thu, May 03, 2018 at 06:34:53PM +0200, Pino Toscano wrote:
> Use the cleanup handlers to free the structs (and list of structs) in
> case their OCaml->C transformation fails for any reason; use calloc()
> to not try to use uninitialized memory.
> 
> In case of lists, avoid allocating the memory for the array if there
> are no elements, since the returned pointer in that case is either NULL,
> or a free()-only pointer; also, set the list size only after the array
> is allocated, to not confuse the XDR routines.
> ---
>  generator/daemon.ml | 41 +++++++++++++++++++++++++----------------
>  1 file changed, 25 insertions(+), 16 deletions(-)
> 
> diff --git a/generator/daemon.ml b/generator/daemon.ml
> index 559ed6898..265f0a475 100644
> --- a/generator/daemon.ml
> +++ b/generator/daemon.ml
> @@ -605,16 +605,18 @@ let generate_daemon_caml_stubs () =
>    (* Implement code for returning structs and struct lists. *)
>    let emit_return_struct typ =
>      let struc = Structs.lookup_struct typ in
> +    let uc_typ = String.uppercase_ascii typ in
>      pr "/* Implement RStruct (%S, _). */\n" typ;
>      pr "static guestfs_int_%s *\n" typ;
>      pr "return_%s (value retv)\n" typ;
>      pr "{\n";
> -    pr "  guestfs_int_%s *ret;\n" typ;
> +    pr "  CLEANUP_FREE_%s guestfs_int_%s *ret = NULL;\n" uc_typ typ;
> +    pr "  guestfs_int_%s *real_ret;\n" typ;
>      pr "  value v;\n";
>      pr "\n";
> -    pr "  ret = malloc (sizeof (*ret));\n";
> +    pr "  ret = calloc (1, sizeof (*ret));\n";
>      pr "  if (ret == NULL) {\n";
> -    pr "    reply_with_perror (\"malloc\");\n";
> +    pr "    reply_with_perror (\"calloc\");\n";
>      pr "    return NULL;\n";
>      pr "  }\n";
>      pr "\n";
> @@ -644,16 +646,20 @@ let generate_daemon_caml_stubs () =
>             pr "  ret->%s = Int_val (v);\n" n
>      ) struc.s_cols;
>      pr "\n";
> -    pr "  return ret;\n";
> +    pr "  real_ret = ret;\n";
> +    pr "  ret = NULL;\n";
> +    pr "  return real_ret;\n";
>      pr "}\n";
>      pr "\n"
>  
>    and emit_return_struct_list typ =
> +    let uc_typ = String.uppercase_ascii typ in
>      pr "/* Implement RStructList (%S, _). */\n" typ;
>      pr "static guestfs_int_%s_list *\n" typ;
>      pr "return_%s_list (value retv)\n" typ;
>      pr "{\n";
> -    pr "  guestfs_int_%s_list *ret;\n" typ;
> +    pr "  CLEANUP_FREE_%s_LIST guestfs_int_%s_list *ret = NULL;\n" uc_typ typ;
> +    pr "  guestfs_int_%s_list *real_ret;\n" typ;
>      pr "  guestfs_int_%s *r;\n" typ;
>      pr "  size_t i, len;\n";
>      pr "  value v, rv;\n";
> @@ -666,32 +672,35 @@ let generate_daemon_caml_stubs () =
>      pr "    rv = Field (rv, 1);\n";
>      pr "  }\n";
>      pr "\n";
> -    pr "  ret = malloc (sizeof *ret);\n";
> +    pr "  ret = calloc (1, sizeof *ret);\n";
>      pr "  if (ret == NULL) {\n";
> -    pr "    reply_with_perror (\"malloc\");\n";
> -    pr "    return NULL;\n";
> -    pr "  }\n";
> -    pr "  ret->guestfs_int_%s_list_len = len;\n" typ;
> -    pr "  ret->guestfs_int_%s_list_val =\n" typ;
> -    pr "    calloc (len, sizeof (guestfs_int_%s));\n" typ;
> -    pr "  if (ret->guestfs_int_%s_list_val == NULL) {\n" typ;
>      pr "    reply_with_perror (\"calloc\");\n";
> -    pr "    free (ret);\n";
>      pr "    return NULL;\n";
>      pr "  }\n";
> +    pr "  if (len > 0) {\n";
> +    pr "    ret->guestfs_int_%s_list_val =\n" typ;
> +    pr "      calloc (len, sizeof (guestfs_int_%s));\n" typ;
> +    pr "    if (ret->guestfs_int_%s_list_val == NULL) {\n" typ;
> +    pr "      reply_with_perror (\"calloc\");\n";
> +    pr "      return NULL;\n";
> +    pr "    }\n";
> +    pr "    ret->guestfs_int_%s_list_len = len;\n" typ;
> +    pr "  }\n";
>      pr "\n";
>      pr "  rv = retv;\n";
>      pr "  for (i = 0; i < len; ++i) {\n";
>      pr "    v = Field (rv, 0);\n";
>      pr "    r = return_%s (v);\n" typ;
>      pr "    if (r == NULL)\n";
> -    pr "      return NULL; /* XXX leaks memory along this error path */\n";
> +    pr "      return NULL;\n";
>      pr "    memcpy (&ret->guestfs_int_%s_list_val[i], r, sizeof (*r));\n" typ;
>      pr "    free (r);\n";
>      pr "    rv = Field (rv, 1);\n";
>      pr "  }\n";
>      pr "\n";
> -    pr "  return ret;\n";
> +    pr "  real_ret = ret;\n";
> +    pr "  ret = NULL;\n";
> +    pr "  return real_ret;\n";
>      pr "}\n";
>      pr "\n";
>    in

Looks good to me so ACK.  May be worth double checking this by running:

  make -C tests/daemon check-valgrind

Rich.

-- 
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
Read my programming and virtualization blog: http://rwmj.wordpress.com
virt-builder quickly builds VMs from scratch
http://libguestfs.org/virt-builder.1.html




More information about the Libguestfs mailing list