tags 747989 + patch
thanks

Hi,

I'm one of the Debian GNUstep maintainers.  Emilio Pozuelo Monfort
from the Release Team pointed me at this bug.

The conclusions at the upstream bug are mostly correct -- Object in
the new runtime is barebone and you either have to implement all the
basic methods with the new libobjc functions, or you have to use
NSObject as your root object (thus depending on GNUstep Base or Cocoa
on MuckOS X).  It is possible to do the former, but that is too much
work for no apparent benefit.  You could take a look at
gnustep-base/Source/NSObject.m for the complexity involved.  There is
too much room for grave errors, even for the basic things.  And the
code would be even more cluttered with #ifdefs.

The patch there attempts to do the latter, but it is not completely
correct and doesn't work in this form:

  - It overrides NSObject's -init, -retain, -release and -isEqual:.
    What you really want is TRObject to become a dummy class fully
    inheriting from NSObject.

  - The autorelease pool should be created in the constructor, before
    the first object is allocated.  That's openvpn_plugin_open_v1,
    as far as I understand the code (but I may be wrong here).  If
    there are additional threads created, you need separate pools for
    every thread.  AFAICS this is not the case here, right?

  - The pool has to be destroyed in the destructor, after releasing
    the last object.  This should be openvpn_plugin_close_v1, but
    again, I'm not familiar with the API and do not understand what's
    the relationship between this function and openvpn_plugin_func_v1
    and when is openvpn_plugin_close_v1 called exactly.

  - The pool is destroyed by sending a -release message.  -drain may
    work, but not in all implementations.

  - With the "modern" runtime, you have two cases only -- Apple and
    non-Apple, because '-framework Foundation' will work only on
    Apple, and you need GNUstep for all other platforms, not only
    GNU/Linux.  That's necessary on Debian too, because we have
    GNU/Hurd and GNU/kFreeBSD.

  - The OBJCFLAGS and linker flags are specific to the GNUstep
    installation, and may differ drastically.  For example, with
    GNUSTEP_INSTALLATION_DOMAIN everything (headers, libraries, tools,
    etc.) is installed in ~/GNUstep.  They also differ depending on
    the compiler/runtime (for instance, the new GNUstep runtime +
    clang) so you'd better resort to gnustep-config for these.  This
    will also ensure that they're always correct and you don't have to
    adjust your package all the time.

With the attached modified version of the upstream patch the plugin
crashes when I run openvpn as advised at
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=641811#72

(gdb) bt
#0  __strlen_ia32 () at
../sysdeps/i386/i686/multiarch/../../i586/strlen.S:94
#1  0xb7b4ccdf in -[LFString initWithCString:] (self=0x80115180, 
    _cmd=0xb7b5e798 <_OBJC_SELECTOR_TABLE+88>, cString=0x0) at
    LFString.m:57
#2  0xb7b4fa08 in -[LFAuthLDAPConfig initWithConfigFile:] (
    self=0xb7b5e748 <_OBJC_SELECTOR_TABLE+8>, 
    _cmd=0xb7b5c1f8 <_OBJC_SELECTOR_TABLE+120>, fileName=0x0)
    at LFAuthLDAPConfig.m:321
#3  0xb7b49e08 in openvpn_plugin_open_v1 (type=0x800c39bc,
argv=0x800c38e4, 
    envp=0x800da19c) at auth-ldap.m:262

That's a general problem with the code -- the check for argv[1] should
be done earlier so that -initWithConfigFile is not called with a NULL
argument and as a result strlen is not called with a NULL argument.
Or the method could first check if the argument is supplied and return
nil immediately if that is not the case.

Running it with the example configuration file:

(gdb) r --dev null --plugin /usr/lib/openvpn/openvpn-auth-ldap.so 
/usr/share/doc/openvpn-auth-ldap/examples/auth-ldap.conf 
Starting program: /usr/sbin/openvpn --dev null --plugin 
/usr/lib/openvpn/openvpn-auth-ldap.so 
/usr/share/doc/openvpn-auth-ldap/examples/auth-ldap.conf
[Thread debugging using libthread_db enabled]
Using host libthread_db library 
"/lib/i386-linux-gnu/i686/cmov/libthread_db.so.1".
Fri Jul  4 00:26:56 2014 OpenVPN 2.3.2 i486-pc-linux-gnu [SSL (OpenSSL)] [LZO] 
[EPOLL] [PKCS11] [eurephia] [MH] [IPv6] built on Mar 17 2014
Fri Jul  4 00:26:57 2014 ******* WARNING *******: all encryption and 
authentication features disabled -- all data will be tunnelled as cleartext
Fri Jul  4 00:26:57 2014 UDPv4 link local (bound): [undef]
Fri Jul  4 00:26:57 2014 UDPv4 link remote: [undef]

And there it stays continuously, I have to interrupt it with C-c.

Please test and let me know if there are problems/questions.  Thanks.
--- openvpn-auth-ldap-2.0.3.orig/aclocal.m4
+++ openvpn-auth-ldap-2.0.3/aclocal.m4
@@ -1,4 +1,3 @@
-builtin(include,objc.m4)
 builtin(include,pthread.m4)
 builtin(include,platform.m4)
 builtin(include,check.m4)
@@ -23,7 +22,7 @@
 #	Result is cached.
 #
 #	Defines one of the following preprocessor macros:
-#		APPLE_RUNTIME GNU_RUNTIME
+#		APPLE_RUNTIME GNU_RUNTIME MODERN_RUNTIME
 #
 #	Substitutes the following variables:
 #		OBJC_RUNTIME OBJC_RUNTIME_FLAGS OBJC_LIBS
@@ -31,7 +30,7 @@
 #------------------------------------------------------------------------
 AC_DEFUN([OD_OBJC_RUNTIME],[
 	AC_REQUIRE([AC_PROG_OBJC])
-	AC_ARG_WITH(objc-runtime, AC_HELP_STRING([--with-objc-runtime], [Specify either "GNU" or "apple"]), [with_objc_runtime=${withval}])
+	AC_ARG_WITH(objc-runtime, AC_HELP_STRING([--with-objc-runtime], [Specify either "GNU", "apple", or "modern"]), [with_objc_runtime=${withval}])
 
 	if test x"${with_objc_runtime}" != x; then
 		case "${with_objc_runtime}" in
@@ -39,8 +38,10 @@
 				;;
 			apple)
 				;;
+			modern)
+				;;
 			*)
-				AC_MSG_ERROR([${with_objc_runtime} is not a valid argument to --with-objc-runtime. Please specify either "GNU" or "apple"])
+				AC_MSG_ERROR([${with_objc_runtime} is not a valid argument to --with-objc-runtime. Please specify either "GNU", "apple", or "modern"])
 				;;
 		esac
 	fi
@@ -183,6 +184,33 @@
 		od_cv_objc_runtime_gnu="no"
 	fi
 
+	if test x"${with_objc_runtime}" = x || test x"${with_objc_runtime}" = x"modern"; then
+		AC_MSG_CHECKING([for Modern Objective C runtime])
+		AC_CACHE_VAL(od_cv_objc_runtime_modern, [
+			# The following uses quadrigraphs
+			# '@<:@' = '['
+			# '@:>@' = ']'
+			AC_LINK_IFELSE([
+					AC_LANG_PROGRAM([
+							#include <objc/objc.h>
+							#include <objc/runtime.h>
+						], [
+							id class = objc_lookUpClass("NSObject");
+							id obj = @<:@class alloc@:>@;
+							puts(@<:@obj name@:>@);
+						])
+					], [
+						od_cv_objc_runtime_modern="yes"
+					], [
+						od_cv_objc_runtime_modern="no"
+					]
+			)
+		])
+		AC_MSG_RESULT(${od_cv_objc_runtime_modern})
+	else
+		od_cv_objc_runtime_modern="no"
+	fi
+
 	# Apple runtime is prefered
 	if test x"${od_cv_objc_runtime_apple}" = x"yes"; then
 			OBJC_RUNTIME="APPLE_RUNTIME"
@@ -194,6 +222,23 @@
 			OBJC_RUNTIME_FLAGS="-fgnu-runtime"
 			AC_MSG_NOTICE([Using GNU Objective-C runtime])
 			AC_DEFINE([GNU_RUNTIME], 1, [Define if using the GNU Objective-C runtime and compiler.]) 
+	elif test x"${od_cv_objc_runtime_modern}" = x"yes"; then
+			OBJC_RUNTIME="MODERN_RUNTIME"
+			case "${target_os}" in
+				darwin*) OBJC_RUNTIME_FLAGS="-fnext-runtime"
+					 LDFLAGS="-framework Foundation ${LDFLAGS}";;
+				*) AC_CHECK_PROG([have_gs_config],
+				                 [gnustep-config], [yes])
+				   if test x"$have_gs_config" != x"yes"; then
+				     AC_MSG_ERROR([Cannot find gnustep-config.])
+				   else
+				   OBJC_RUNTIME_FLAGS="`gnustep-config --objc-flags`"
+				   OBJC_LIBS="`gnustep-config --base-libs`"
+				   fi
+				   ;;
+			esac
+			AC_MSG_NOTICE([Using Modern Objective-C runtime])
+			AC_DEFINE([MODERN_RUNTIME], 1, [Define if using the Modern Objective-C runtime and compiler.])
 	else
 			AC_MSG_FAILURE([Could not locate a working Objective-C runtime.])
 	fi
--- openvpn-auth-ldap-2.0.3.orig/src/TRObject.h
+++ openvpn-auth-ldap-2.0.3/src/TRObject.h
@@ -40,20 +40,29 @@
 #endif
 
 #include <stdbool.h>
+#ifdef MODERN_RUNTIME
+#import <Foundation/NSObject.h>
+#else
 #include <objc/Object.h>
+#endif
 
 #include "auth-ldap.h"
 
 @protocol TRObject
+#ifndef MODERN_RUNTIME
 /* Reference counting */
 - (id) retain;
 - (void) release;
 
 /* Equality */
 - (BOOL) isEqual: (id) anObject;
+#endif
 @end
 
 
+#ifdef MODERN_RUNTIME
+@interface TRObject : NSObject <TRObject>
+#else
 @interface TRObject : Object <TRObject> {
 	unsigned int _refCount;
 }
@@ -66,6 +75,7 @@
 
 - (void) dealloc;
 
+#endif
 @end
 
 #endif /* TROBJECT_H */
--- openvpn-auth-ldap-2.0.3.orig/src/TRObject.m
+++ openvpn-auth-ldap-2.0.3/src/TRObject.m
@@ -53,6 +53,7 @@
  * Additionally, we implement brain-dead, non-thread-safe
  * reference counting.
  */ 
+#ifndef MODERN_RUNTIME
 @interface Object (AppleAddedAReallyStupidGCCWarning)
 - (void) dealloc;
 @end
@@ -102,4 +103,7 @@
 		[self dealloc];
 }
 
+#else
+@implementation TRObject
+#endif
 @end
--- openvpn-auth-ldap-2.0.3.orig/src/auth-ldap.m
+++ openvpn-auth-ldap-2.0.3/src/auth-ldap.m
@@ -48,6 +48,11 @@
 #include <TRPacketFilter.h>
 #include <TRPFAddress.h>
 #include <TRLog.h>
+#ifdef MODERN_RUNTIME
+#import <Foundation/NSAutoreleasePool.h>
+
+static NSAutoreleasePool *pool;
+#endif
 
 /* Plugin Context */
 typedef struct ldap_ctx {
@@ -166,7 +171,7 @@
 	const char userFormat[] = "%u";
 
 	/* Copy the template */
-	templateString = [[LFString alloc] initWithString: template];
+	templateString = [(LFString *)[LFString alloc] initWithString: template];
 
 	/* Initialize the result */
 	result = [[LFString alloc] init];
@@ -250,6 +255,9 @@
 
 OPENVPN_EXPORT openvpn_plugin_handle_t
 openvpn_plugin_open_v1(unsigned int *type, const char *argv[], const char *envp[]) {
+#ifdef MODERN_RUNTIME
+        pool = [[NSAutoreleasePool alloc] init];
+#endif
 	ldap_ctx *ctx = xmalloc(sizeof(ldap_ctx));
 	ctx->config = [[LFAuthLDAPConfig alloc] initWithConfigFile: argv[1]];
 	if (!ctx->config) {
@@ -606,5 +614,8 @@
 		[ldapUser release];
 	if (ldap)
 		[ldap release];
+#ifdef MODERN_RUNTIME
+	[pool release];
+#endif
 	return (ret);
 }
--- openvpn-auth-ldap-2.0.3.orig/tests/Makefile.in
+++ openvpn-auth-ldap-2.0.3/tests/Makefile.in
@@ -26,7 +26,7 @@
 CFLAGS+=	@CHECK_CFLAGS@ -DTEST_DATA=\"${srcdir}/data\"
 OBJCFLAGS+=	@CHECK_CFLAGS@ -DTEST_DATA=\"${srcdir}/data\"
 LIBS+=		-lauth-ldap-testing $(OBJC_LIBS) $(LDAP_LIBS) @CHECK_LIBS@
-LDFLAGS+=	-L${top_builddir}src $(LIBS)
+LDFLAGS+=	-L${top_builddir}/src $(LIBS)
 
 # Recompile the tests every time
 all: tests
--- openvpn-auth-ldap-2.0.3.orig/src/Makefile.in
+++ openvpn-auth-ldap-2.0.3/src/Makefile.in
@@ -64,7 +64,8 @@
 	$(INSTALL_PLUGIN)
 
 clean::
-	rm -f $(TEST_OBJS) $(PLUGIN_OBJS) $(TEST_LIB) $(GEN_SRCS) testplugin
+	rm -f $(TEST_OBJS) $(PLUGIN_OBJS) $(PLUGIN_OBJS:.o=.d) $(TEST_LIB) \
+	  $(GEN_SRCS) testplugin
 	$(CLEAN_PLUGIN)
 
 distclean:: clean

Reply via email to