Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Added Linux kernel source excerpt

...

The benefits of this method are that the code is cleaner and we prevent the rewriting of similar code upon every function error.

...

Compliant Solution (Linux kernel)

Many effective examples of goto-chains are quite large. The following compliant code is an excerpt from the Linux kernel. This is the copy_process function from kernel/fork.c from Version 2.6.29 of the kernel.

The function uses 17 goto labels (not all displayed here) in order to perform cleanup code should any internal function yield an error code. If no errors occur, the program returns a pointer to the new process p. If any error occurs, the program diverts control to a particular goto label, which performs cleanup for the sections of the function that have currently been successfully executed, while not performing cleanup on sections of the function that have not been executed yet. Consequently only resources that were successfully opened are actually closed.

All comments in this excerpt were added to indicate additional code in the kernel not displayed here.

Code Block
bgcolor#ffcccc

static struct task_struct *copy_process(unsigned long clone_flags,
					unsigned long stack_start,
					struct pt_regs *regs,
					unsigned long stack_size,
					int __user *child_tidptr,
					struct pid *pid,
					int trace)
{
  int retval;
  struct task_struct *p;
  int cgroup_callbacks_done = 0;

  if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))
    return ERR_PTR(-EINVAL);

  /* ... */

  retval = security_task_create(clone_flags);
  if (retval)
    goto fork_out;

  retval = -ENOMEM;
  p = dup_task_struct(current);
  if (!p)
    goto fork_out;

  /* ... */

  /* copy all the process information */
  if ((retval = copy_semundo(clone_flags, p)))
    goto bad_fork_cleanup_audit;
  if ((retval = copy_files(clone_flags, p)))
    goto bad_fork_cleanup_semundo;
  if ((retval = copy_fs(clone_flags, p)))
    goto bad_fork_cleanup_files;
  if ((retval = copy_sighand(clone_flags, p)))
    goto bad_fork_cleanup_fs;
  if ((retval = copy_signal(clone_flags, p)))
    goto bad_fork_cleanup_sighand;
  if ((retval = copy_mm(clone_flags, p)))
    goto bad_fork_cleanup_signal;
  if ((retval = copy_namespaces(clone_flags, p)))
    goto bad_fork_cleanup_mm;
  if ((retval = copy_io(clone_flags, p)))
    goto bad_fork_cleanup_namespaces;
  retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);
  if (retval)
    goto bad_fork_cleanup_io;

  /* ... */

  return p;

  /* ... cleanup code starts here ... */

bad_fork_cleanup_io:
  put_io_context(p->io_context);
bad_fork_cleanup_namespaces:
  exit_task_namespaces(p);
bad_fork_cleanup_mm:
  if (p->mm)
    mmput(p->mm);
bad_fork_cleanup_signal:
  cleanup_signal(p);
bad_fork_cleanup_sighand:
  __cleanup_sighand(p->sighand);
bad_fork_cleanup_fs:
  exit_fs(p); /* blocking */
bad_fork_cleanup_files:
  exit_files(p); /* blocking */
bad_fork_cleanup_semundo:
  exit_sem(p);
bad_fork_cleanup_audit:
  audit_free(p);

  /* ... more cleanup code ... */

fork_out:
  return ERR_PTR(retval);
}
. Once such example can be found in the linux kernel (v2.6.xx) in the function copy_process() in kernel/fork.c.

Risk Assessment

Failure to free allocated memory or close opened files results in a memory leak and possibly unexpected results.

...

Wiki Markup
\[[ISO/IEC 9899:1999|AA. C References#ISO/IEC 9899-1999]\] Section 7.20.3, "Memory management functions"
\[[ISO/IEC 9899:1999|AA. C References#ISO/IEC 9899-1999]\] Section 7.19.5, "File access functions"
[Linux kernel Sourcecode (v2.6.xx)|http://www.kernel.org/pub/linux/kernel/v2.6] 2.6.29, {{kernel/fork.c}}, the {{copy_process()}} function
\[[Seacord 05|AA. C References#Seacord 05]\] Chapter 4, "Dynamic Memory Management"