Fix 'crypt' deadlock upon error.
authorLudovic Courtès <ludo@gnu.org>
Fri, 22 Dec 2017 10:12:57 +0000 (11:12 +0100)
committerLudovic Courtès <ludo@gnu.org>
Fri, 22 Dec 2017 10:12:57 +0000 (11:12 +0100)
* libguile/posix.c (scm_crypt): Take 'scm_i_misc_mutex' right before
calling 'crypt'.  Move 'SCM_SYSERROR' call after 'scm_dynwind_end'.
* test-suite/tests/posix.test ("crypt"): New test prefix.

libguile/posix.c
test-suite/tests/posix.test

index 041b8b129d017b8bf7ca3a31c948a19dd1176c15..c39fc57b55ecfdf44515630b5780e01b194a205e 100644 (file)
@@ -1,6 +1,6 @@
 /* Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
  *   2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013,
- *   2014, 2016 Free Software Foundation, Inc.
+ *   2014, 2016, 2017 Free Software Foundation, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License
@@ -1935,26 +1935,43 @@ SCM_DEFINE (scm_crypt, "crypt", 2, 0, 0,
            "crypt(3) library call.")
 #define FUNC_NAME s_scm_crypt
 {
+  int err;
   SCM ret;
   char *c_key, *c_salt, *c_ret;
 
   scm_dynwind_begin (0);
-  scm_i_dynwind_pthread_mutex_lock (&scm_i_misc_mutex);
 
   c_key = scm_to_locale_string (key);
   scm_dynwind_free (c_key);
   c_salt = scm_to_locale_string (salt);
   scm_dynwind_free (c_salt);
 
+  /* Take the lock because 'crypt' uses a static buffer.  */
+  scm_i_dynwind_pthread_mutex_lock (&scm_i_misc_mutex);
+
   /* The Linux crypt(3) man page says crypt will return NULL and set errno
      on error.  (Eg. ENOSYS if legal restrictions mean it cannot be
      implemented).  */
   c_ret = crypt (c_key, c_salt);
+
   if (c_ret == NULL)
-    SCM_SYSERROR;
+    /* Note: Do not throw until we've released 'scm_i_misc_mutex' since
+       this would cause a deadlock down the path.  */
+    err = errno;
+  else
+    {
+      err = 0;
+      ret = scm_from_locale_string (c_ret);
+    }
 
-  ret = scm_from_locale_string (c_ret);
   scm_dynwind_end ();
+
+  if (err != 0)
+    {
+      errno = err;
+      SCM_SYSERROR;
+    }
+
   return ret;
 }
 #undef FUNC_NAME
index f57001a241d215d79b9f256a73c15e2aba724b07..4dadd7784b196aab318d27ea10d609f9c6c0844c 100644 (file)
@@ -1,7 +1,7 @@
 ;;;; posix.test --- Test suite for Guile POSIX functions. -*- scheme -*-
 ;;;;
 ;;;; Copyright 2003, 2004, 2006, 2007, 2010, 2012,
-;;;;   2015 Free Software Foundation, Inc.
+;;;;   2015, 2017 Free Software Foundation, Inc.
 ;;;;
 ;;;; This library is free software; you can redistribute it and/or
 ;;;; modify it under the terms of the GNU Lesser General Public
     (let ((me (getpid)))
       (and (not (zero? (system* "something-that-does-not-exist")))
            (= me (getpid))))))
+
+;;
+;; crypt
+;;
+
+(with-test-prefix "crypt"
+
+  (pass-if "basic usage"
+    (string? (crypt "pass" "abcdefg")))
+
+  (pass-if-exception "glibc EINVAL" exception:system-error
+    ;; This used to deadlock while trying to throw to 'system-error'.
+    ;; This test uses the special interpretation of the salt that glibc
+    ;; does; specifically, we pass a syntactically invalid salt here.
+    (if (string-contains %host-type "-gnu")
+        (crypt "pass" "$X$abc")                   ;EINVAL
+        (throw 'unresolved))))