Hallo,
DESCRIPTION: getanswer_r ends in infinite loop on certain inputs.
GLIBC_VERSION: 2.11.3
Note: I am novice into glibc and don't have complete understanding of glibc with respect to dns resolution.
IN DETAIL:
When I execute iptables-restore on one of our Lab's system, iptables-restore started consuming 100% cpu all the time. I did strace on iptables-restore I found that most of the cpu is comsumed by NSS DNS resolver of libc as given below.
67.50% iptables-restor libnss_dns-2.11.3.so [.] getanswer_r
15.12% iptables-restor libc-2.11.3.so [.] __strcasecmp
9.02% iptables-restor libc-2.11.3.so [.] __ctype_b_loc
7.02% iptables-restor libc-2.11.3.so [.] __i686.get_pc_thunk.bx
0.77% iptables-restor libnss_dns-2.11.3.so [.] 0x00000c00
0.18% iptables-restor [kernel.kallsyms] [k] read_hpet
To get more details, I attached iptables-restore to gdb with debugging symbols loaded and found that glibc takes most of the CPU, since it ends in endless loop. when,
1. have_answer is set
2. net_i is set to BYNAME
3. result->n_aliases is set to ns5.dsredirections.com ( i,e in the code ap is set to ns5.dsredirections.com )
With this input, I found that the loop never exists and continue to process the same input again and again.
Basically as per the code comment, the loop functionality is to :
/* Check each alias name for being of the forms:
4.3.2.1.in-addr.arpa = net 1.2.3.4
3.2.1.in-addr.arpa = net 0.1.2.3
2.1.in-addr.arpa = net 0.0.1.2
1.in-addr.arpa = net 0.0.0.1
*/
But then the question is :
1. Is this is a valid input or not to getanswer_r.
on the other hand, when the input is of bad form like ns5.dsredirections.com, code has to detect and come out of loop.
FINDINGS and SOLUTION:
I found that the most recent Upstream code does not have this fixed as this part of the code is stable since 2002.
** I tried to patch glibc as given below. ( Not sure if its correct and does not break other scenarios )
avoid infinite loop for invalid entry.
getaddr_r gets into infinite loop for invalid entries
like ns5.dsredirections.com then it never detects and
breaks from infinite loop.
This patch detects the entries with non digit and
non hexadecimal digits and returns.
diff --git a/glibc/glibc-2.11.3-getaddr.diff b/glibc/glibc-2.11.3-getaddr.diff
new file mode 100644
index 0000000..664b033
--- /dev/null
+++ b/glibc/glibc-2.11.3-getaddr.diff
@@ -0,0 +1,42 @@
+Index: glibc-2.11.3/resolv/nss_dns/dns-network.c
+===================================================================
+--- glibc-2.11.3.orig/resolv/nss_dns/dns-network.c
++++ glibc-2.11.3/resolv/nss_dns/dns-network.c
+@@ -414,6 +414,7 @@ getanswer_r (const querybuf *answer, int
+ uint32_t val = 0; /* Accumulator for n_net value. */
+ unsigned int shift = 0; /* Which part we are parsing now. */
+ const char *p = *ap; /* Consuming the string. */
++ unsigned int invalid = 0;
+ do
+ {
+ /* Match the leading 0 or 0[xX] base indicator. */
+@@ -440,12 +441,23 @@ getanswer_r (const querybuf *answer, int
+ part = (part * base) + (*p - '0');
+ else if (base == 16 && isxdigit (*p))
+ part = (part << 4) + 10 + (tolower (*p) - 'a');
++
++ /* when neither digit nor hexadigit, then its a invalid */
++ if ( !isdigit(*p) && !isxdigit(*p) && *p != '.')
++ {
++ invalid = 1;
++ }
++
+ ++p;
+ } while (*p != '\0' && *p != '.');
+
++ if( invalid )
++ return NSS_STATUS_NOTFOUND;
++
+ if (*p != '.')
+ break; /* Bad form. Give up on this name. */
+
++
+ /* Install this as the next more significant byte. */
+ val |= part << shift;
+ shift += 8;
+@@ -470,4 +482,4 @@ getanswer_r (const querybuf *answer, int
+
+ __set_h_errno (TRY_AGAIN);
+ return NSS_STATUS_TRYAGAIN;
+-}
++}
After the patched glibc is installed, iptables-restore does not run infinitely and everything is normal.
Could you please let me know, whether this will be fixed moving further.
I would like to extend my help with any information if needed.
Best,
Yash