1+ #ifndef _GNU_SOURCE
2+ #define _GNU_SOURCE 1
3+ #endif
4+
15#include <ctype.h>
26#include <getopt.h>
37#include <errno.h>
1418#include <sys/time.h>
1519#include <unistd.h>
1620#include <fcntl.h>
21+ #if defined(__unix__ ) || defined(__APPLE__ )
22+ #include <dlfcn.h>
23+ #endif
1724#include "xtables.h"
1825#include <math.h>
1926#include "xshared.h"
2027
28+ #define XS_LONGOPTS_SCAN_LIMIT 4096U
29+
30+ #if defined(__unix__ ) || defined(__APPLE__ )
31+ #define XS_HAVE_DLADDR 1
32+ #else
33+ #define XS_HAVE_DLADDR 0
34+ #endif
35+
36+ static size_t xs_longopts_count (const struct option * opts , const char * ext_name )
37+ {
38+ size_t i ;
39+
40+ if (opts == NULL )
41+ return 0 ;
42+
43+ for (i = 0 ; i < XS_LONGOPTS_SCAN_LIMIT ; ++ i ) {
44+ if (opts [i ].name == NULL )
45+ return i ;
46+ }
47+
48+ if (ext_name != NULL )
49+ xtables_error (OTHER_PROBLEM ,
50+ "Extension \"%s\" returned an unterminated option table." ,
51+ ext_name );
52+
53+ xtables_error (OTHER_PROBLEM ,
54+ "xtables option table is missing its terminator." );
55+ return 0 ;
56+ }
57+
58+ #if XS_HAVE_DLADDR
59+ static bool xs_option_name_pointer_is_valid (const char * name )
60+ {
61+ Dl_info info ;
62+
63+ if (name == NULL )
64+ return true;
65+
66+ return dladdr ((const void * )name , & info ) != 0 ;
67+ }
68+ #else
69+ static bool xs_option_name_pointer_is_valid (const char * name )
70+ {
71+ if (name == NULL )
72+ return true;
73+
74+ #if INTPTR_MAX > 0xffffffff
75+ if ((uintptr_t )name < 0x100000000ULL )
76+ return false;
77+ #endif
78+ return true;
79+ }
80+ #endif
81+
82+ static void xs_validate_new_longopts (struct option * opts , size_t start ,
83+ const char * ext_name )
84+ {
85+ size_t total ;
86+ size_t i ;
87+
88+ if (opts == NULL || ext_name == NULL )
89+ return ;
90+
91+ total = xs_longopts_count (opts , ext_name );
92+ if (start >= total )
93+ return ;
94+
95+ for (i = start ; i < total ; ++ i ) {
96+ if (!xs_option_name_pointer_is_valid (opts [i ].name )) {
97+ xtables_error (OTHER_PROBLEM ,
98+ "Extension \"%s\" was built against an incompatible libxtables release (detected corrupt option metadata). Please rebuild the module." ,
99+ ext_name );
100+ }
101+ }
102+ }
103+
21104/*
22105 * Print out any special helps. A user might like to be able to add a --help
23106 * to the commandline, and see expected results. So we call help for all
@@ -146,6 +229,7 @@ int command_default(struct iptables_command_state *cs,
146229 m = load_proto (cs );
147230 if (m != NULL ) {
148231 size_t size ;
232+ size_t merge_start ;
149233
150234 cs -> proto_used = 1 ;
151235
@@ -157,18 +241,22 @@ int command_default(struct iptables_command_state *cs,
157241 m -> m -> u .user .revision = m -> revision ;
158242 xs_init_match (m );
159243
244+ merge_start = xs_longopts_count (gl -> opts , NULL );
160245 if (m -> x6_options != NULL )
161246 gl -> opts = xtables_options_xfrm (gl -> orig_opts ,
162247 gl -> opts ,
163248 m -> x6_options ,
164249 & m -> option_offset );
165250 else
166- gl -> opts = xtables_merge_options (gl -> orig_opts ,
167- gl -> opts ,
168- m -> extra_opts ,
169- & m -> option_offset );
251+ gl -> opts = xs_merge_options (gl -> orig_opts ,
252+ gl -> opts ,
253+ m -> extra_opts ,
254+ & m -> option_offset );
170255 if (gl -> opts == NULL )
171256 xtables_error (OTHER_PROBLEM , "can't alloc memory!" );
257+ xs_validate_new_longopts (gl -> opts , merge_start ,
258+ m -> real_name != NULL ?
259+ m -> real_name : m -> name );
172260 optind -- ;
173261 /* Indicate to rerun getopt *immediately* */
174262 return 1 ;
@@ -564,18 +652,26 @@ void command_match(struct iptables_command_state *cs)
564652 if (m == m -> next )
565653 return ;
566654 /* Merge options for non-cloned matches */
567- if (m -> x6_options != NULL ){
568- opts = xtables_options_xfrm (xt_params -> orig_opts , opts ,
655+ {
656+ size_t merge_start = xs_longopts_count (opts , NULL );
657+
658+ if (m -> x6_options != NULL ) {
659+ opts = xtables_options_xfrm (xt_params -> orig_opts , opts ,
569660 m -> x6_options , & m -> option_offset );
570- int num_orig ;
571- for (num_orig = 0 ; opts [num_orig ].name != NULL ; ++ num_orig ) {}
661+ } else if (m -> extra_opts != NULL ) {
662+ opts = xs_merge_options (xt_params -> orig_opts , opts ,
663+ m -> extra_opts , & m -> option_offset );
664+
665+ } else
666+ return ;
572667
668+ if (opts == NULL )
669+ xtables_error (OTHER_PROBLEM , "can't alloc memory!" );
670+
671+ xs_validate_new_longopts (opts , merge_start ,
672+ m -> real_name != NULL ?
673+ m -> real_name : m -> name );
573674 }
574- else if (m -> extra_opts != NULL )
575- opts = xtables_merge_options (xt_params -> orig_opts , opts ,
576- m -> extra_opts , & m -> option_offset );
577- if (opts == NULL )
578- xtables_error (OTHER_PROBLEM , "can't alloc memory!" );
579675 xt_params -> opts = opts ;
580676}
581677
@@ -628,15 +724,85 @@ void command_jump(struct iptables_command_state *cs)
628724 cs -> target -> t -> u .user .revision = cs -> target -> revision ;
629725 xs_init_target (cs -> target );
630726
631- if (cs -> target -> x6_options != NULL )
727+ size_t merge_start = xs_longopts_count (opts , NULL );
728+
729+ if (cs -> target -> x6_options != NULL ) {
632730 opts = xtables_options_xfrm (xt_params -> orig_opts , opts ,
633- cs -> target -> x6_options ,
634- & cs -> target -> option_offset );
635- else
636- opts = xtables_merge_options (xt_params -> orig_opts , opts ,
731+ cs -> target -> x6_options ,
732+ & cs -> target -> option_offset );
733+ } else if ( cs -> target -> extra_opts != NULL )
734+ opts = xs_merge_options (xt_params -> orig_opts , opts ,
637735 cs -> target -> extra_opts ,
638736 & cs -> target -> option_offset );
737+ else
738+ return ;
639739 if (opts == NULL )
640740 xtables_error (OTHER_PROBLEM , "can't alloc memory!" );
741+ xs_validate_new_longopts (opts , merge_start ,
742+ cs -> target -> real_name != NULL ?
743+ cs -> target -> real_name : cs -> jumpto );
641744 xt_params -> opts = opts ;
642745}
746+
747+
748+ struct option *
749+ xs_merge_options (struct option * orig_opts , struct option * oldopts ,
750+ const struct option * newopts , unsigned int * option_offset )
751+ {
752+ unsigned int num_orig = 0 , num_old = 0 , num_new = 0 , i ;
753+ struct option * merge ;
754+ struct option * mp ;
755+ struct option * old_base = oldopts ;
756+
757+ if (newopts == NULL || newopts -> name == NULL )
758+ return oldopts ;
759+
760+ if (orig_opts != NULL )
761+ for (; orig_opts [num_orig ].name != NULL ; ++ num_orig )
762+ ;
763+
764+ if (oldopts != NULL )
765+ for (; oldopts [num_old ].name != NULL ; ++ num_old )
766+ ;
767+
768+ for (; newopts [num_new ].name != NULL ; ++ num_new )
769+ ;
770+
771+ if (oldopts != NULL && num_old >= num_orig ) {
772+ oldopts += num_orig ;
773+ num_old -= num_orig ;
774+ } else {
775+ oldopts = NULL ;
776+ num_old = 0 ;
777+ }
778+
779+ merge = malloc (sizeof (* merge ) * (num_orig + num_new + num_old + 1 ));
780+ if (merge == NULL )
781+ return NULL ;
782+
783+ if (num_orig != 0 )
784+ memcpy (merge , orig_opts , sizeof (* merge ) * num_orig );
785+ mp = merge + num_orig ;
786+
787+ xt_params -> option_offset += XT_OPTION_OFFSET_SCALE ;
788+ * option_offset = xt_params -> option_offset ;
789+
790+ for (i = 0 ; i < num_new ; ++ i , ++ mp ) {
791+ mp -> name = newopts [i ].name ;
792+ mp -> has_arg = newopts [i ].has_arg ;
793+ mp -> flag = newopts [i ].flag ;
794+ mp -> val = newopts [i ].val + * option_offset ;
795+ }
796+
797+ if (oldopts != NULL && num_old != 0 ) {
798+ memcpy (mp , oldopts , sizeof (* mp ) * num_old );
799+ mp += num_old ;
800+ }
801+
802+ memset (mp , 0 , sizeof (* mp ));
803+
804+ if (old_base != NULL && old_base != orig_opts && old_base != xt_params -> orig_opts )
805+ free (old_base );
806+
807+ return merge ;
808+ }
0 commit comments