> Synopsis:     iked.conf(5) incompletely documents comment syntax and has 
> potentially problematic behavior where comments can be continued with line 
> continuations (\), leading to unexpected configuration parsing. Man page also 
> fails to document that comments can have preceding whitespace and appear at 
> end of lines.
> Category:     bin
> Description:
        The iked.conf(5) man page currently states:
        > Lines beginning with ‘#’ and empty lines are regarded as comments, 
and ignored.  Lines may be split using the ‘\’ character.
        
        However, the parser actually allows for
        - comments to be continued with line continuations `\\\n` (Possibly a 
bug)
        - preceding white space
        - trailing comments
        
        # Comments Continued with Line Continuations (Possible Bug)
        
        A comment that uses a line continuation will keep reading into the next 
line instead of stopping at the newline character.
        Most programmers would expect a comment to end at the newline, so this 
could cause hard-to-find bugs.
        
        For example:
        ```conf
        ikev2 \
            from dynamic to any \
            peer 10.0.0.1 \
        #   request address 10.1.0.10 \
            iface lo1
        ```
        In this IKE config, you might expect the commented address line to be 
ignored, but the line continuation causes `iface lo1` to be part of the comment 
and get ignored too.
        
        ```conf
        ikev2 \
            from dynamic to any \
            peer 10.0.0.1 # temporary IP for dev environment \
            request address 10.1.0.10 \
            iface lo1
        ```
        Here, placing an explanatory comment before the line actually comments 
out both the address line and the interface line due to the line continuation.
        
        Both of these can lead to a subtly broken configuration.
        
        ## Comment and Line Continuation Processing in the Lexer
        
        The lexer handles comments and line continuations at different levels 
of processing:
        
        ### Comment Processing (in `yylex`)
        ```c
        if (c == '#')
            while ((c = lgetc(0)) != '\n' && c != EOF)
                ; /* nothing */
        ```
        This simple loop discards all characters after a `#` until it hits a 
newline or EOF.
        
        ### Line Continuation Processing (in `lgetc`)
        ```c
        while ((c = igetc()) == '\\') {
            next = igetc();
            if (next != '\n') {
                c = next;
                break;
            }
            yylval.lineno = file->lineno;
            file->lineno++;
        }
        ```
        When encountering a backslash, the code checks if it's followed by a 
newline. If so, it updates the line counter and continues processing, 
effectively joining the lines. If not, it preserves the character after the 
backslash.
        
        ## Interaction and Precedence
        
        Since `yylex` uses `lgetc` to read input, line continuations are 
processed before comment detection. This leads to an important behavior:
        
        ```c
        # comment with a continuation \
        still a comment
        ```
        The line continuation is processed first, making both lines part of the 
comment. This layered processing design makes sense structurally but creates 
potentially unexpected behavior where comments can continue across line 
continuations, which might surprise users expecting comments to be strictly 
line-based.
        
        # Preceding White Space
        
        Preceding white space is allowed before comments.
        
        ```iked.conf
            # This is a comment with preceding spaces
                # This is a comment with a preceding tab
        ```
        
        The relevant code is in parse.y at the beginning of `yylex`
        
        ```c
            while ((c = lgetc(0)) == ' ' || c == '\t')
                ; /* nothing */
            
            yylval.lineno = file->lineno;
            if (c == '#')
                while ((c = lgetc(0)) != '\n' && c != EOF)
                    ; /* nothing */
        ```
        
        # Trailing Comments
        
        Trailing comments are actually allowed.
        
        ```iked.conf
        ikev2 esp from 10.0.0.0/24 to 192.168.1.0/24  # This is a comment
        ```
        
        It works by:
        1. `yyparse` calls `yylex` to get each token it needs
        2. After getting a token that completes a valid grammar rule, `yyparse` 
calls `yylex` again
        3. In this subsequent call, `yylex` first skips whitespace:
                ```c
                while ((c = lgetc(0)) == ' ' || c == '\t')
                    ; /* nothing */
                ```
        4. Then if it finds a comment, it consumes/discards it:
                ```c
                if (c == '#')
                    while ((c = lgetc(0)) != '\n' && c != EOF)
                        ; /* nothing */
                ```
        
        So trailing comments are allowed not by any special grammar rule or 
comment-aware tokens, but simply by the fact that `yyparse` keeps calling 
`yylex` and `yylex` naturally discards whitespace and comments at the start of 
each tokenization attempt.
> Fix:
        If treating comments with line continuations as a bug:
        
        Update iked.conf.5 to
        
        > Lines beginning with '#' and empty lines are regarded as comments, 
and ignored. Lines may be split using the '\' character, except within comments 
which always terminate at a newline regardless of line continuations. This 
allows individual options within a continued line to be commented out without 
commenting out the entire continued line.
        
        Update parse.y yylex so comments contain the continuation and are not 
continued by it
        
        ```diff
        -if (c == '#')
        -    while ((c = lgetc(0)) != '\n' && c != EOF)
        -        ; /* nothing */
        +if (c == '#') {
        +    do {
        +        c = igetc(); /* Use igetc directly to ignore line 
continuations */
        +    } while (c != '\n' && c != EOF);
        +    if (c == EOF)
        +        return (0);
        +}
        ```
        
        If comments should continue with line continuations:
        
        Update iked.conf.5 to
        
        > Lines beginning with '#' and empty lines are regarded as comments, 
and ignored. Lines may be split using the '\' character, including within 
comments which will continue across line continuations. The parser will log a 
warning when a comment contains a line continuation to help prevent unintended 
comment continuation.
        
        Update parse.y yylex so comments that end with a continuation emit a 
warning
        
        ```diff
        -if (c == '#')
        -    while ((c = lgetc(0)) != '\n' && c != EOF)
        -        ; /* nothing */
        +if (c == '#') {
        +    while ((c = lgetc(0)) != '\n' && c != EOF) {
        +        if (c == '\\') {
        +            if ((c = igetc()) == '\n') {
        +                log_info("line %d: comment contains line continuation 
- "
        +                    "following line will be part of comment", 
file->lineno);
        +                file->lineno++;
        +            } else {
        +                lungetc(c);
        +                c = '\\';
        +            }
        +        }
        +    }
        +    if (c == EOF)
        +        return (0);
        +}
        ```

Reply via email to