Skip to contentSkip to conversation

change thread-unsafe readdir to thread-safe readdir_r calls

PATCH In review Steven Dake 3 participants 5 messages Jul 7, 2010 – Jul 21, 2010 3 messages not in the archive InfiniBand

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 change thread-unsafe readdir to thread-safe readdir_r calls

From: Steven Dake <sdake-AqqL/iNfm4CbU/[email protected]>

The readdir POSIX api is not thread safe. This presents problems in multithreaded programs that use the libibverbs APIs. This patch has been tested with a libibverbs application (http://www.corosync.org) on Mellanox MT26428 cards.

This updated version uses alloca instead of malloc.

---
 src/init.c |   12 ++++++++++--
 1 files changed, 10 insertions(+), 2 deletions(-)
src/init.c +10 -2
index 4f0130e..11b8415 100644
@@ -35,6 +35,9 @@
 #  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
+#if HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
 #include <stdlib.h>
 #include <string.h>
 #include <glob.h>
@@ -46,6 +49,7 @@
 #include <sys/time.h>
 #include <sys/resource.h>
 #include <dirent.h>
+#include <stddef.h>
 #include <errno.h>
 
 #include "ibverbs.h"
@@ -85,6 +89,7 @@ static int find_sysfs_devs(void)
 	struct ibv_sysfs_dev *sysfs_dev = NULL;
 	char value[8];
 	int ret = 0;
+	struct dirent *buf;
 
 	snprintf(class_path, sizeof class_path, "%s/class/infiniband_verbs",
 		 ibv_get_sysfs_path());
@@ -93,7 +98,8 @@ static int find_sysfs_devs(void)
 	if (!class_dir)
 		return ENOSYS;
 
-	while ((dent = readdir(class_dir))) {
+	buf = alloca(offsetof(struct dirent, d_name) + NAME_MAX + 1);
+	while (readdir_r(class_dir, buf, &dent) == 0 && dent) {
 		struct stat buf;
 
 		if (dent->d_name[0] == '.')
@@ -294,6 +300,7 @@ static void read_config(void)
 {
 	DIR *conf_dir;
 	struct dirent *dent;
+	struct dirent *buf;
 	char *path;
 
 	conf_dir = opendir(IBV_CONFIG_DIR);
@@ -303,7 +310,8 @@ static void read_config(void)
 		return;
 	}
 
-	while ((dent = readdir(conf_dir))) {
+	buf = alloca(offsetof(struct dirent, d_name) + NAME_MAX + 1);
+	while (readdir_r(conf_dir, buf, &dent) == 0 && dent) {
 		struct stat buf;
 
 		if (asprintf(&path, "%s/%s", IBV_CONFIG_DIR, dent->d_name) < 0) {
Signature · 6 lines
1.6.6.1

--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

A message that is not in the archive (sent off-list or not yet received): 1278540873-3857-1-git-send-email-sdake-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org

#2 lore raw
Quoted 2 lines

The readdir POSIX api is not thread safe. This presents problems in multithreaded programs that use the libibverbs APIs.

So as discussed this is not true ... as far as I can see libibverbs only calls readdir inside a mutex and so on all platforms where libibverbs has even a chance of being applicable, the current code works fine. The real issue is that corosync defines a new environment where readdir is not allowed.

I'm really not sure about this patch one way or another. With alloca, it ends up looking pretty clean, but we do introduce the tiny possibility of buffer overrun (since readdir_r uses a user-supplied buffer of poorly-specified size); the benefit is that we work within corosync's constraints. I honestly don't have a good feeling whether the benefit exceeds the cost.

Jason, any opinion one way or another?

src/init.c line 40

Quoted 3 lines
src/init.c +3 -0
+#if HAVE_ALLOCA_H
+#include <alloca.h>
+#endif

Elsewhere we just include alloca.h unconditionally, and in fact I don't see anything that would define HAVE_ALLOCA_H in the libibverbs tree. Does this actually work?

- R.

Signature · 6 lines
Roland Dreier <[email protected]> || For corporate legal information go to:
http://www.cisco.com/web/about/doing_business/legal/cri/index.html
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

A message that is not in the archive (sent off-list or not yet received): adalj9lzv4r.fsf-BjVyx320WGW9gfZ95n9DRSW4+XlvGpQz@public.gmane.org

#4 lore raw
Quoted 8 lines · Roland Dreier

I'm really not sure about this patch one way or another. With alloca, it ends up looking pretty clean, but we do introduce the tiny possibility of buffer overrun (since readdir_r uses a user-supplied buffer of poorly-specified size); the benefit is that we work within corosync's constraints. I honestly don't have a good feeling whether the benefit exceeds the cost.

Jason, any opinion one way or another?

Well, my view is that this is needless churn/risk for a very bad choice made by another project. Aliasing a perfectly thread safe readdir() to assert is anti-social :)

I also do not think it enhances theoretical portability, as NAME_MAX is not guarenteed to exist by POSIX and alloca is not a C or POSIX standard function. (C99 stadardized alloca as variable sized arrays)

Frankly, as a Linux only low level library, relying on glibc behavior to make it simpler and more correct is perfectly acceptable to me..

It isn't even the case that libibverbs is an outlier here, common things like libcrypto even use readdir! Purging readdir from every library your app might want to load via a plugin is a really futile quest :(

Jason
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html

#5 lore raw
In reply to a message not in the archive

src/init.c static int find_sysfs_devs(void) · line 102

Quoted 2 lines
src/init.c +2 -0
+	buf = alloca(offsetof(struct dirent, d_name) + NAME_MAX + 1);
+	while (readdir_r(class_dir, buf, &dent) == 0 && dent) {

So after thinking this over, I don't think I'm going to apply this patch. I think the right fix is for corosync to allow readdir() -- in general pushing people to safer APIs is probably a good thing, but I think in this particular case it doesn't make sense. In fact it seems that readdir_r() is actually worse since the "buf" parameter does not have a well-defined required size, while readdir() is actually safe in most uses.

- R.

Signature · 6 lines
Roland Dreier <[email protected]> || For corporate legal information go to:
http://www.cisco.com/web/about/doing_business/legal/cri/index.html
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

A message that is not in the archive (sent off-list or not yet received): adak4ooogjj.fsf-BjVyx320WGW9gfZ95n9DRSW4+XlvGpQz@public.gmane.org

#7 lore raw
Quoted 11 lines · Roland Dreier
Quoted 2 lines

+ buf = alloca(offsetof(struct dirent, d_name) + NAME_MAX + 1);
+ while (readdir_r(class_dir, buf,&dent) == 0&& dent) {

So after thinking this over, I don't think I'm going to apply this patch. I think the right fix is for corosync to allow readdir() -- in general pushing people to safer APIs is probably a good thing, but I think in this particular case it doesn't make sense. In fact it seems that readdir_r() is actually worse since the "buf" parameter does not have a well-defined required size, while readdir() is actually safe in most uses.

Quoted 1 line

- R.

thanks for considering

regards
-steve
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html