バッファオーバーフローの解決法

解決


みけにゃん  URL  2005-03-08 18:22:55  No: 56618

添付ファイルの展開はうまくいったのですが、本文を含めて64KBを
超えるファイルがくると展開時にバッファオーバーフローで
現状のプログラムでは落ちてしまいます。

また、2個以上の添付ファイルがある場合も落ちてしまうのですが
これは分割メールの添付ファイル展開でするのでしょうか?
ご教授よろしくお願いします。

以下がそのコードです。(http://angelteatime.punyu.net/knot/file/mview005.lzhと同じです)

// 添付ファイルを展開して保存ダイアログ
LRESULT CALLBACK AttachFileProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
    HWND hList, hFDEdit, hFBtn, hFDBtn;
    
    BROWSEINFO bi;
    ITEMIDLIST *lpid;
    HRESULT hr;
    LPMALLOC pMalloc = NULL;    // IMallocへのポインタ
    BOOL bDir = FALSE;

    HANDLE hFile;                // ファイルハンドル
    HGLOBAL hMem;                // グローバルハンドル
    DWORD dwFSizeHigh, dwFSize, dwAccBytes;    // バイト数
    char *lpszBuf;                // ファイルバッファ
    char *f_id, *PertId;        // 分割メールのID
    int no;                        // 添付ファイルの個数
    int ret;                    // 戻り値

    // 添付メール展開用変数
    char subject[TEMP_MAX], date[TEMP_MAX], from[TEMP_MAX], header[TEMP_MAX], body[TEMP_MAX];
    char FileName[TEMP_MAX], Temp[NMAIL_ATTACHMENT_TEMP_SIZE];

    switch(msg){
        case WM_INITDIALOG:
            CenterWindow(hDlg, hParent);
            hList = GetDlgItem(hDlg, IDC_LIST1);
            hFDBtn = GetDlgItem(hDlg, IDC_FILEBTN);
            hFBtn = GetDlgItem(hDlg, IDC_BUTTON1);
            return TRUE;
        case WM_COMMAND:
            switch(LOWORD(wParam)){
            case IDC_BUTTON1:
                MessageBox(hDlg, "添付ファイルの展開は実装されていません", "Mail Viewer", MB_OK | MB_ICONINFORMATION);
                AttachFileOpen(hDlg, szAtFile, szAtFileName);

                // ファイルを開く
                hFile = CreateFile(szAtFile, GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
                
                if(hFile == INVALID_HANDLE_VALUE){
                    MessageBox(hDlg, "ファイルをオープンできません", "Error", MB_OK);
                    return FALSE;
                }
                
                // ファイルサイズを調べる
                dwFSize = GetFileSize(hFile, &dwFSizeHigh);
                
                if(dwFSizeHigh != 0){
                    MessageBox(hDlg, "ファイルが大きすぎます", "Error", MB_OK);
                    CloseHandle(hFile);
                    return FALSE;
                }

                // ファイルを読み込むためのメモリ領域を確保
                hMem = GlobalAlloc(GHND, dwFSize + 1);
                
                if(hMem == NULL){
                    MessageBox(hDlg, "メモリを確保できません", "Error", MB_OK);
                    CloseHandle(hFile);
                    return FALSE;
                }
                
                // メモリ領域をロックしてファイルを読み込む
                lpszBuf = (char *)GlobalLock(hMem);
                ReadFile(hFile, lpszBuf, dwFSize, &dwAccBytes, NULL);
                lpszBuf[dwFSize] = '\0';
                
                // メールファイルの中身をコピーする
                strcpy((char *)szAtFileData, lpszBuf);
                
                // ファイルを閉じる
                CloseHandle(hFile);
                GlobalUnlock(hMem);
                GlobalFree(hMem);
                
                // ファイルが添付されているかを確認する
                if((no = NMailAttachmentFileStatus(szAtFileData, f_id, TEMP_MAX)) != NMAIL_NO_ATTACHMENT_FILE){
                    if(no == 1){
                        PertId = f_id;
                    }
                    hList = GetDlgItem(hDlg, IDC_LIST1);
                    if(no == 0 || PertId == f_id){
                        SendMessage(hList, LB_INSERTSTRING, (WPARAM)0, (LPARAM)szAtFile);
                        SendMessage(hList, LB_SETCURSEL, (WPARAM)0, 0L);
                        EnableWindow(GetDlgItem(hDlg, IDOK), TRUE);
                    }
                }
                else{
                    // 添付ファイルがないのでメッセージを出して終了
                    MessageBox(hDlg, "このメールに、添付ファイルはありません", "添付ファイルなし", MB_OK |MB_ICONINFORMATION);
                }

                return TRUE;

            case IDC_FILEBTN:
                MessageBox(hDlg, "添付ファイルの展開は実装されていません", "Mail Viewer", MB_OK | MB_ICONINFORMATION);
                memset(&bi, 0, sizeof(BROWSEINFO));
                bi.hwndOwner = hDlg;
                bi.lpfn = SHMyProc;
                bi.ulFlags = BIF_EDITBOX | BIF_STATUSTEXT | BIF_VALIDATE;
                bi.lpszTitle = "展開先ディレクトリ指定";
                lpid = SHBrowseForFolder(&bi);
                
                if(lpid == NULL){
                    return FALSE;
                }
                else{
                    hr = SHGetMalloc(&pMalloc);
                    if(hr == E_FAIL){
                        MessageBox(hDlg, "SHGetMalloc Error", "Error", MB_OK);
                        return FALSE;
                    }
                    SHGetPathFromIDList(lpid, szDir);
                    if(szDir[strlen(szDir) - 1] != '\\'){
                        strcat(szDir, "\\");
                    }

                    hFDEdit = GetDlgItem(hDlg, IDC_EDIT1);

                    // 展開先フォルダをセットする
                    SetWindowText(hFDEdit, szDir);

                    pMalloc->Free(lpid);
                    pMalloc->Release();
                    bDir = TRUE;
                }
                return TRUE;

                case IDOK:
                    // 添付ファイルの展開
                    ret = NMailAttachmentFileFirst(Temp, subject, date, from, header, body, szDir, FileName, szAtFileData, NULL);
                    if(ret == NMAIL_SUCCESS){
                        NMailAttachmentFileClose(Temp);
                        wsprintf(strMsg, "%sに%sを展開しました。", szDir, FileName);
                        MessageBox(hDlg, strMsg, "Mail Viewer", MB_OK | MB_ICONINFORMATION);
                    }
                    EndDialog(hDlg, IDOK);
                    return TRUE;
                case IDCANCEL:
                    EndDialog(hDlg, IDOK);
                    return TRUE;
                default:
                    return FALSE;
            }
            default:
                return FALSE;
    }
    return TRUE;
}


みけにゃん  URL  2005-03-08 18:49:00  No: 56619

自己レスです

>また、2個以上の添付ファイルがある場合も落ちてしまうのですが
>これは分割メールの添付ファイル展開でするのでしょうか?

今Excelファイルとテキストファイルを添付したメールを
ThunderBirdから送ってMailViewerで保存して展開をしてみましたが
NMailAttachmentFileFirstで複数のファイルを展開できました。


YOU  2005-03-09 00:40:37  No: 56620

よく見てはいませんが、
TEMP_MAX が 65536 だからではないですか?
領域オーバーしていませんか?


瀬戸っぷ  2005-03-09 08:17:40  No: 56621

> 添付ファイルの展開はうまくいったのですが、本文を含めて64KBを
> 超えるファイルがくると展開時にバッファオーバーフローで

ヘッダも込みで64KBのハズです。

> >また、2個以上の添付ファイルがある場合も落ちてしまうのですが
> 今Excelファイルとテキストファイルを添付したメールを
> ThunderBirdから送ってMailViewerで保存して展開をしてみましたが
> NMailAttachmentFileFirstで複数のファイルを展開できました。

単に64KBに収まったってだけだと思われます。
対処療法ではどうしようもないので、まずは原因を調べましょう。

と言っても、YOUさんが既にレスしていますが、
> TEMP_MAX が 65536 だからではないですか?
で、添付ファイル展開の際にファイルを読み込んで
コピーする領域が64KBしか用意していないから、
それより大きい場合にオーバーフローするコトになります。

じゃあ、TEMP_MAX増やせば…というのは解決になりません。
ファイルの読み込み処理のところでやっているように動的確保して下さい。
動的確保でローカル変数にハンドル等を入れると寿命の問題が発生しますので要注意です。


みけにゃん  URL  2005-03-09 23:53:57  No: 56622

>ファイルの読み込み処理のところでやっているように動的確保して下さい。
>動的確保でローカル変数にハンドル等を入れると寿命の問題が発生しますので
>要注意です。

動的確保ということはGlobalAlloc()とかを使って
該当する変数の領域を広げると言うことでしょうか?
ここの所(ファイルを開く部分)は猫でも分かるのファイル読み込みを
使ってるだけなので、それ以外での使い方がいまいち分かりません。(T_T)

本当に申し訳ないです。


瀬戸っぷ  2005-03-10 02:15:18  No: 56623

> 動的確保ということはGlobalAlloc()とかを使って
> 該当する変数の領域を広げると言うことでしょうか?

静的確保(注:static変数という意味では無い。参考 http://www.ncad.co.jp/~komata/c-kouza5.htm )した領域を
広げることは出来ません。
(C99規格なら可能…なのかな?どっかで見たけど。)
char szAtFile[TEMP_MAX]のように固定的に確保するのではなく、
ポインタを用意して確保して下さい。
例えば、char *lpAtFile とでもして、読み込み時に動的確保します。
もちろん、不要になったら解放する必要があります。

> ここの所(ファイルを開く部分)は猫でも分かるのファイル読み込みを
> 使ってるだけなので、それ以外での使い方がいまいち分かりません。(T_T)

メモリの動的確保はいろいろなところで使われますのでしっかり理解しておいた方がいいと思われます。
参考 http://www.ncad.co.jp/~komata/c-kouza6.htm
(↑内容見るとUNIXなどのX-Window関係とおぼしき関数名が出てた入りしますが。)
猫でものC言語編 第33章とか。
他にも… http://www.google.co.jp/search?hl=ja&c2coff=1&q=%E3%83%A1%E3%83%A2%E3%83%AA+%E5%8B%95%E7%9A%84%E7%A2%BA%E4%BF%9D&btnG=Google+%E6%A4%9C%E7%B4%A2&lr=lang_ja
でいっぱい見つけられるでしょう。

# 読み込み時に動的確保しているので、保持したまま渡した方が
# 内容をコピーする分の時間とメモリが節約できると思いますが。
# コレのウィンドウが複数開くことがなければstatic変数を使うことで
# わりと簡単に対応できます。static変数の代りにグローバル変数でも可能だけど。
## あまし関係ないけど、GlobalLock()/GlobalUnlock()とかが面倒でGPTRとか使う(爆)
## というか最近はもっぱらHeapAlloc()とかの方だけど。


みけにゃん  URL  2005-03-10 04:46:35  No: 56624

WisdomSoftさんのページの動的メモリのページを見て
一応解決しました。mallocでファイルサイズ分szAtFileDataを
領域を取るようにして実行したら、メールと添付されている
ファイルのサイズ合計が64KB以上でも正しく添付ファイルを
取り出すことが出来ました。

でも気になるのは例のf_idの警告が消えないということです。
次は画面系でスプリットコントロールの質問をしようと思います。


瀬戸っぷ  2005-03-10 07:47:42  No: 56625

> でも気になるのは例のf_idの警告が消えないということです。

指しているアドレスが不定だから…でしょう。
NMailAttachmentFileStatus()の挙動についてはよく判らないので
開発元に確認して下さい。


みけにゃん  URL  2005-03-10 20:25:55  No: 56626

nMail.hlpの添付ファイルに関してのQ&Aを見ると
f_idと同じ意味を持つid1はTEMP_MAXでメモリ確保していました。
もしかしたらメモリ確保をすれば、警告は消えるのかもしれません。

f_id = (char *)malloc(sizeof(char) * TEMP_MAX);

if((no = NMailAttachmentFileStatus(szAtFileData, f_id, sizeof(f_id))) != NMAIL_NO_ATTACHMENT_FILE){
(以下略)

今気付いた事ですが、メール受信で使用しているRead_Mail関数で
添付ファイルがあるときはメール本文だけを取り出すことをやっていて
その部分でsid(ここでいうf_id)はTEMP_MAX分メモリが確保されていました。

char sid[TEMP_MAX], AtHeader[TEMP_MAX], AtBody[TEMP_MAX], AtSubject[TEMP_MAX];

if(NMailAttachmentFileStatus(header, sid, sizeof(sid)) >= 0){
(以下略)


みけにゃん  URL  2005-03-10 20:34:22  No: 56627

連続投稿でごめんなさい
コード的にはこのような感じでしょうか?

f_id = (char *)malloc(sizeof(char) * TEMP_MAX);

if(f_id == NULL){
   MessageBox(hDlg, "メモリの確保に失敗しました", "メモリ確保失敗", MB_OK | MB_ICONSTOP);
   return FALSE;
}

        // ファイルが添付されているかを確認する
        if((no = NMailAttachmentFileStatus(szAtFileData, f_id, sizeof(f_id))) != NMAIL_NO_ATTACHMENT_FILE){
          if(no == 1){
            PertId = f_id;
          }
          hList = GetDlgItem(hDlg, IDC_LIST1);
          if(no == 0 || PertId == f_id){
            SendMessage(hList, LB_INSERTSTRING, (WPARAM)0, (LPARAM)szAtFile);
            SendMessage(hList, LB_SETCURSEL, (WPARAM)0, 0L);
            EnableWindow(GetDlgItem(hDlg, IDOK), TRUE);
          }
        }
        else{
          // 添付ファイルがないのでメッセージを出して終了
          MessageBox(hDlg, "このメールに、添付ファイルはありません", "添付ファイルなし", MB_OK |MB_ICONINFORMATION);
        }

(以下略)
// f_idのメモリを解放する
free(f_id);


瀬戸っぷ  2005-03-10 23:58:44  No: 56628

> コード的にはこのような感じでしょうか?

おそらくOKだと思います。
が、
> NMailAttachmentFileStatus()の挙動についてはよく判らないので
> 開発元に確認して下さい。
というコトで、私自身には問題なしと断言は出来ません。

# 添付ファイル付きの分割メールって…
# 1通のメールサイズが制限されている場合に分割される…ってコトだろうか?
# 最近、そういう例を見た記憶がない。
# ので私の作っているメールチェッカーも未対応ってコトに。
## ちなみにまた時間なくなるので検証している余裕はありません。


瀬戸っぷ  2005-03-11 00:05:45  No: 56629

> おそらくOKだと思います。

ローカル変数の寿命が問題……でしたかね。
PertId の値は、場合によっては不定値になってます。
ソレを参照する処理には問題が……


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加