Tại sao môi trường của tôi đầy s?


7

Tôi có một quá trình có môi trường như sau:

root@a-vm:/proc/1363# hexdump -C environ
00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
0000016c

Tôi chưa bao giờ thấy bất cứ điều gì như thế này; Tôi hy vọng environcó chứa key=valuecác cặp kết thúc nul , vì vậy đầu ra này vi phạm tất cả các loại xác nhận. Tôi đang xem xét một lỗi kernel đã biết, hay có một cách hợp pháp nào đó trong Unix / Linux để thực hiện điều này? (Càng và nếu vậy, tại sao? Tại sao hạt nhân thậm chí cho phép điều này vô nghĩa?)

(trên Linux, 3.13.0 / Ubuntu Trusty)

(Tôi chạy vào này trong khi cố gắng tìm ra lý do tại sao quá trình này không được viết một số lượng tạm thời để đúng vị trí, nó giả sử để sử dụng một thư mục nhất định cho việc lưu trữ tạm thời, và nó thông báo của thư mục đó qua thiết lập các biến env TMP; nhưng tôi' Tôi đang thiết lập TMPmột thứ gì đó trông giống như một con đường rất bình thường, không phải là một mớ hỗn độn và tôi chưa bao giờ thấy một env nào hoàn toàn trống rỗng.)


/ Proc / <pid> / môi trường là môi trường ban đầu cho quy trình. Từ đầu ra, có vẻ như quá trình với pid 1363 đã được bắt đầu với một môi trường ban đầu trống rỗng.
Andy Dalton

2
Nếu đó là sự thật, thì tập tin sẽ trống hay chỉ một ␀ chứ không phải hàng trăm trong số chúng?
Thanatos

Phụ thuộc vào nếu bạn vượt qua một con trỏ NULL hoặc nếu bạn vượt qua một bộ đệm sẽ null ký tự. Nếu bạn vượt qua NULL, thì có, bạn sẽ thấy một tệp trống.
Andy Dalton

Câu trả lời:


16

Điều này không phải là vô nghĩa, có một cách hợp pháp trong Linux để thực hiện điều này và kỳ vọng của bạn là sai lầm.

Chuỗi đối số và môi trường được truyền vào mã khởi động của chương trình bởi kernel được lưu trữ trong bộ nhớ ảo không gian ứng dụng thông thường, giống như bất kỳ dữ liệu chương trình nào khác; và, giống như bất kỳ biến dữ liệu chương trình nào khác, chúng có thể sửa đổi. Nó là khá hợp pháp cho các chương trình để sửa đổi chúng.

(Lưu ý rằng đây là từ quan điểm về những gì hạt nhân cung cấp và thi hành. Những tiêu chuẩn cho các ngôn ngữ lập trình cụ thể có thể nói không nhất thiết phải giống nhau. Nhưng theo như kernel, đó chỉ là một lĩnh vực của không gian ứng dụng bộ nhớ ảo cho dữ liệu chương trình có thể đọc và ghi được. Hạt nhân không quan tâm ngôn ngữ lập trình bạn đã biên dịch mã máy từ đâu.)

Các /proc/${PID}/environtập tin chỉ là một cửa sổ vào bộ nhớ ảo không gian ứng dụng này. Thay vì nhớ dữ liệu môi trường thực tế của quá trình, Linux chỉ nhớ các địa chỉ bắt đầu và kết thúc của khu vực môi trường mà nó đã bắt đầu quá trình và /proc/${PID}/environtệp chỉ đọc bất cứ thứ gì có trong bộ nhớ đó ngay bây giờ. Bạn không nên mong đợi rằng tệp này chứa danh sách các chuỗi kết thúc.. Đó là một kỳ vọng sai lầm.

Không có chức năng thư viện GNU C để sửa đổi bộ nhớ chứa các chuỗi này. Nhưng các chương trình khác nhau có chức năng riêng của họ để làm như vậy.

Ví dụ, xem xét OpenSSH. Máy chủ OpenSSH sửa đổi những gì pshiển thị cho vectơ đối số của nó, để đọc những thứ như sshd: JdeBP [priv].

Máy chủ OpenSSH chứa mã cố gắng bắt chước trên Linux những gì nó có thể làm với thư viện BSD C trên OpenBSD. Trên OpenBSD có một hàm thư viện BSD C có tên setproctitle()là viết lại vectơ đối số quy trình như được báo cáo bởi pslệnh. Nó gọi sysctl()để truyền một vectơ đối số mới cho kernel, pscó thể đọc ra sysctl(). FreeBSD có chức năng tương tự.

Trên Linux, như đã giải thích, kernel không nhớ các đối số và môi trường thực tế, chỉ đơn thuần là địa chỉ bắt đầu và kết thúc của các vùng nhớ nơi ban đầu đặt chúng khi bắt đầu quá trình. Vì vậy, cổng Linux của OpenSSH có setproctitle()chức năng tương thích ghi đè lên vùng nhớ trên, thay vào đó.

Hàm tương thích này tính toán tổng kích thước của vùng môi trường vùng đối số và ghi đè lên tất cả vùng đó bằng chuỗi đối số mới. Nó thực hiện điều này bởi vì trong các trường hợp thông thường, các chương trình gọi setproctitle()muốn viết trong một tập hợp dữ liệu đối số dài hơn so với quy trình ban đầu. sshdthường làm. Vì vậy, nó cho phép các đối số mới ghi đè lên vùng môi trường theo sau vùng đối số, giúp các chương trình có nhiều chỗ hơn cho các chuỗi đối số dài hơn.

Điều quan trọng, nó cũng đệm phần không sử dụng của khu vực mà nó không cần ghi đè lên, theo chiều dài ban đầu của tổng dữ liệu đối số và môi trường, với ␀s.

Và những gì bạn đang thấy là kết quả chính xác của điều này. Nếu bạn tìm thấy quy trình máy chủ OpenSSH trên hệ thống của mình, bạn cũng sẽ thấy rằng nó cũng có rất nhiều ␀ trong đó /proc/${PID}/environ.

đọc thêm


1
Cần lưu ý rằng việc setproctitletriển khai một số dự án kéo vào Linux (ví dụ thông qua libbsd) đều có đầy đủ các hành vi không xác định nghiêm trọng và không nên được sử dụng. Bộ nhớ ghi đè không thuộc về bạn (ví dụ: thư viện chuẩn, trình liên kết động, v.v. có thể có tài liệu tham khảo) vì mục đích hiển thị các thông điệp đẹp trong đầu ra ps / top là hoàn toàn không thể thực hiện được.
R .. GitHub DỪNG GIÚP ICE

2
@R .. Tôi không thấy cách ghi đè bộ nhớ đó sẽ là hành vi không xác định C, ngay cả khi các phần khác của quy trình có tham chiếu đến nó. Bạn có thể giải thích?
marcelm

1
@marcelm: OK. Để bắt đầu, chúng ta phải sử dụng POSIX, vì nếu không thì không có environcon trỏ nào để đến bộ nhớ. Trên environ, POSIX nói: "Bất kỳ ứng dụng nào trực tiếp sửa đổi các con trỏ mà các điểm biến môi trường có hành vi không xác định." Dường như không được phép sửa đổi các chuỗi trỏ (chịu các ràng buộc đồng bộ hóa bộ nhớ), nhưng không rõ liệu nó có được xác định để lại chúng ở dạng không phải là mục nhập môi trường hợp lệ hay không "X=Y".
R .. GitHub DỪNG GIÚP ICE

1
Một điều chắc chắn là không hợp lệ (được xác định rõ) là thực hiện số học con trỏ qua cuối chuỗi environ[0]và / hoặc dựa vào nó để chuyển sang (và có thể được coi là một đối tượng kết hợp với) environ[1]. Cụ thể, nếu environ[0]không bị chấm dứt null sau khi thay đổi nó (như setproctitlecó thể gây ra sau khi đặt văn bản thông báo lên trên nó) thì số học con trỏ ngoài giới hạn sẽ xảy ra sau khi chuỗi được truy cập.
R .. GitHub DỪNG GIÚP ICE

2

Điều này là hoàn toàn có thể thực hiện được bằng cách ghi NULs vào vị trí bộ nhớ mà các biến môi trường nằm trong:

#include <stdio.h>
#include <unistd.h>

extern char **environ;

int main(void)
{
    int i;
    char *p = *environ;
    /* hopefully your ENV is longer than this */
    for (i = 0; i < 10; i++) *(p + i) = 0;
    printf("hexdump -C /proc/%d/environ\n", getpid());
    sleep(99999);
}

Thay vào đó, nếu bạn bắt đầu một chương trình với một môi trường trống, thì environtệp sẽ hoàn toàn trống:

execle("/bin/sleep", "sleep", "999", (char *)NULL, (char *const) NULL)

Vì vậy, trường hợp này là một cái gì đó được thực hiện bởi hoặc cho quá trình một khi nó đang chạy (và có rất ít để ngăn chặn điều này xảy ra trừ khi bạn bằng cách nào đó khóa bộ nhớ đó và sau đó setenv(3)các cuộc gọi có thể có vấn đề ...).


Tôi nghĩ bạn sẽ thấy điều này là sai. /proc/$pid/environkhông (và không có cách nào để thực hiện, vì thực thi thậm chí có thể không có ký hiệu) theo dõi environđối tượng và theo dõi con trỏ. Nó chỉ đơn giản là bỏ các nội dung của phạm vi bộ nhớ mà hạt nhân được sử dụng để truyền vào các giá trị môi trường ban đầu.
R .. GitHub DỪNG GIÚP ICE

@R .. nếu nó sai, thì tại sao hexdumptrên Centos 6 lại hiển thị chính xác 10 NULđược viết trên toàn bộ môi trường cho quá trình?
thrig

Ồ, xin lỗi, tôi đã đọc sai nó. Nó thực sự ghi đè lên các chuỗi được chỉ bởi environ[0], chứ không phải các con trỏ trong environ[]chính nó.
R .. GitHub DỪNG GIÚP ICE
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.