Created attachment 4760 [details] demo program The connman 1.10 package in buildroot uses the res_nclose function as part of shutting down it's internal resolver representation. Since commit 1f9808dbf74fb771f6a631a4f37a2f2c3b9e4643 this function has a noreturn attribute, and a while(1)..; at the end of it, causing it to spin forever and use 100% cpu when res_nclose is called. This doesn't seem correct and the noreturn attribute and the loop should probably be removed. I'm using uClibc daily snapshot 20120213, built by buildroot 2012.02_rc1, with the attached .config. Specifically, gcc 4.6.3 and binutils 2.21.1. Also attached is a little demo program which works when compiled with my host toolchain, but hangs forever after printing 'Initialised' when compiled with uClibc and run on my target.
Created attachment 4766 [details] uClibc .config
(In reply to comment #0) > Created attachment 4760 [details] > demo program > > The connman 1.10 package in buildroot uses the res_nclose function as part of > shutting down it's internal resolver representation. Since commit > 1f9808dbf74fb771f6a631a4f37a2f2c3b9e4643 this function has a noreturn > attribute, and a while(1)..; at the end of it, causing it to spin forever and > use 100% cpu when res_nclose is called. > > This doesn't seem correct and the noreturn attribute and the loop should > probably be removed. yes, looks like.
Created attachment 4772 [details] bug-5954a.patch revert aforementioned commit
Created attachment 4778 [details] bug-5954b.patch Wouldn't we need to also clear that res_state that you pass in like in the b.patch? Consider your testcase: #include <sys/types.h> #include <netinet/in.h> #include <arpa/nameser.h> #include <resolv.h> #include <netdb.h> #include <assert.h> int main(int argc, char **argv) { int r; struct __res_state state; r = res_ninit(&state); if (r < 0) herror("init"); printf("Initialised\n"); #ifdef __UCLIBC__ assert (state._u._ext.nscount >= 0); #else /* Looks like glibc did not populate the nameservers yet, perhaps since there was no query yet? FIX the testcase, make a query against that user-res-state.. */ assert (state._u._ext.nscount == 0); #endif res_nclose(&state); printf("Closed\n"); assert (state._u._ext.nscount == 0); return 0; } That connman application does odd things, i guess it should not need to do that at all in the first place..
Yes - it looks like res_nclose should close the res_state that is passed, and not the global one. Connman only uses the res_ninit/res_nclose with a user res_state so that it can read the nameservers out to add to its own internal list. So connman won't care what really happens in res_nclose, but I imagine other applications might get an unpleasant surprise if res_nclose closes the global state, not the user-supplied one. On closer inspection, connman expects different behaviour in the population of res_state than what uClibc provides. It expects res.nscount and res.nsaddr_list to contain both v4 and v6 nameservers, and only gets the ipv6 address from res._u._ext.nsaddrs[i]->sin6_addr if res.nsaddr_list[i].sin_family == AF_INET6. Example largely created from code taken from connman: /etc/resolv.conf: nameserver 127.0.0.1 nameserver ::1 nameserver ::2 restest2.c: #include <sys/types.h> #include <netinet/in.h> #include <arpa/nameser.h> #include <resolv.h> #include <netdb.h> int main(int argc, char **argv) { int r, i; struct __res_state state; r = res_ninit(&state); if (r < 0) herror("init"); printf("Initialised: %d nameservers, %d ext nameservers\n", state.nscount, state._u._ext.nscount); for (i = 0; i < state.nscount; i++) { char buf[100]; int family = state.nsaddr_list[i].sin_family; void *sa_addr = &state.nsaddr_list[i].sin_addr; if (family != AF_INET && state._u._ext.nsaddrs[i]) { family = AF_INET6; sa_addr = &state._u._ext.nsaddrs[i]->sin6_addr; } if (family != AF_INET && family != AF_INET6) continue; if (inet_ntop(family, sa_addr, buf, sizeof(buf))) printf("Read nameserver: %s\n", buf); } res_nclose(&state); printf("Closed: %d nameservers, %d ext nameservers\n", state.nscount, state._u._ext.nscount); return 0; } glibc output: Initialised: 3 nameservers, 0 ext nameservers Read nameserver: 127.0.0.1 Read nameserver: ::1 Read nameserver: ::2 Closed: 3 nameservers, 0 ext nameservers uClibc output: Initialised: 1 nameservers, 3 ext nameservers Read nameserver: 127.0.0.1 Closed: 1 nameservers, 3 ext nameservers I haven't got time to try to put together a patch now, but I can when I return from holiday in 2 weeks.
I suppose this was fixed on master by 3d791bda2e68136e5cfc52b5386e0db805b5d3ba and 950fcf0f68732a9aafacb5dfd33e7a7612141722 thanks!