0

FIFO を使用してサーバーを再現する C プログラムがあります。このプログラムは、入力 FIFO から 2 行 (数値nと文字列) を読み取り、出力 FIFO行にstr書き込みます。次のコードを書きました。nstr

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>

#define MAX_SIZE 256

char *readline(int fd, char *buffer) {
    char c;
    int i = 0;
    while (read(fd, &c, 1) != 0) {
        if (c == '\n')
            break;
        buffer[i++] = c;
    }
    return buffer;
}

int main(int  argc, char** argv) {
    mkfifo(argv[1], 0666);
    mkfifo(argv[2], 0666);
    int in = open(argv[1], O_RDONLY);
    int out = open(argv[2] ,O_WRONLY);

    char line[MAX_SIZE];
    memset(line, 0, MAX_SIZE);
    int n, i;
    while (1) {
        strcpy(line, readline(in, line));
        sscanf(line, "%d", &n);
        strcpy(line, readline(in, line));

        for (i = 0; i < n; i++) {
            write(out, line, strlen(line));
            write(out, "\n", 1);
        }
    }
    close(in);
    close(out);

    return 0;
}

このプログラムはエラーなしでコンパイルおよび実行されますが、実行ごとに異なる数の文字列を出力します。たとえば、入力 FIFO の 2 つの入力行が である場合、実行ごと5\nhelloに 1 ~ 25 回の が出力されhelloます (頻度は完全にランダムに見えます)。

私はこれに2日間立ち往生しています。助けてください。

4

3 に答える 3

2

システム レベルで FIFO を使用する差し迫った必要性が生じてから 20 年が経過したため、私はあなたのプログラム何をするかさえ知っていると主張したり保証したりしません。しかし、ひとつだけはっきりしていることがあります。デバッガーで実行せずに何かに取り組むのに 2 日というのは長い時間です。デバッガーで実行すると、多くの問題が明らかになります。

まず、readline()渡された文字列を決して終了させません。入力行には短いデータが存在する可能性があるため、これは 2 回目以降ほど重要ではありません。さらに、read()失敗する可能性があり、そうすると0、ループが壊れる唯一の条件である が返されません。その失敗はループ中断し、返される結果に反映されるはずです。バッファ ポインタを返すため、妥当な失敗結果は NULL になる可能性があります。

次のようなことを考えてみましょう:

char *readline(int fd, char *buffer)
{
    ssize_t res = 0;
    char c = 0;
    int i = 0;
    for(;;)
    {
        res = read(fd, &c, 1);
        if (res < 0)
            return NULL;
        else if (res == 0 || c == '\n')
            break;
        buffer[i++] = c;

    };
    buffer[i] = 0;
    return buffer;
}

長さ 0 のパケットを FIFO に置くことはできないため、バッファが空の場合は NULL を返す必要があると主張する人もいます。それはあなたの判断にお任せしますが、これはあなたのアルゴリズムに潜在的な穴があることは確かです.


次に、送信されたバッファがオーバーラップする場合、strcpy()関数は未定義の動作をします。渡されたまさにそのreadline()バッファーを返し、同じバッファーがstrcpy()is the same bufferのターゲットでもあるため、プログラムは UB を実行しています。私が見るすべてから、strcpy()そもそもこのプログラムでは役に立たず、まったくそこにいるべきではありません.

これは明らかに間違っています:

strcpy(line, readline(in, line));
sscanf(line, "%d", &n);
strcpy(line, readline(in, line));

for (i = 0; i < n; i++) {
    write(out, line, strlen(line));
    write(out, "\n", 1);
}

上記は次のようになります。

if (readline(in, line))
{
    if (sscanf(line, "%d", &n) == 1)
    {
        if (readline(in, line))
        {
            for (i = 0; i < n; i++) 
            {
                write(out, line, strlen(line));
                write(out, "\n", 1);
            }
        }
    }
}

readline()規定どおりに変更が行われたと仮定します。これらは 1 つの 3 つの式の if 句に結合できますが、上記のように、少なくともデバッグ可能です。言い換えれば、短絡評価を介してこれを行うのに問題はないはずです:

if (readline(in, line) &&
    sscanf(line, "%d", &n) == 1 &&
    readline(in, line))
{
    for (i = 0; i < n; i++) 
    {
        write(out, line, strlen(line));
        write(out, "\n", 1);
    }
}

ただし、これを完全にデバッグするまでは、前者を保持することをお勧めします。

readline()最後に、まだバッファ オーバーフローが発生するのを待っていることに注意してください。その関数に max-len を渡して潜在的な可能性を制限するか、バッファを動的に管理する必要があります。

于 2013-11-10T14:09:05.363 に答える