प्रतिनिधि इंटरव्यू विषय

सामान्य साक्षात्कार: Unix डोमेन सॉकेट्स फ़ाइल डिस्क्रिप्टर्स को कैसे पास करते हैं?

सामान्यकठिन
Offer.cc संपादकीय टीमप्रकाशित अपडेट किया गया

प्रश्न

दो स्थानीय प्रक्रियाएं एक Unix डोमेन सॉकेट पर समन्वय करती हैं: एक विशेषाधिकार प्राप्त प्रक्रिया एक फ़ाइल खोलती है या एक लिसनिंग सॉकेट बनाती है और इसे कम-विशेषाधिकार प्राप्त वर्कर को सौंपती है। बताएं कि डिस्क्रिप्टर को कैसे पास किया जाए, रिसीवर को क्या मिलता है, और ट्रंकेशन और ऑथराइजेशन जोखिमों को कैसे संभाला जाए।

प्रॉम्प्ट और संदर्भ

दो स्थानीय प्रक्रियाएं एक Unix डोमेन सॉकेट पर समन्वय करती हैं: एक विशेषाधिकार प्राप्त प्रक्रिया एक फ़ाइल खोलती है या एक लिसनिंग सॉकेट बनाती है और इसे कम-विशेषाधिकार प्राप्त वर्कर को सौंपती है। बताएं कि डिस्क्रिप्टर को कैसे पास किया जाए, रिसीवर को क्या मिलता है, और ट्रंकेशन और ऑथराइजेशन जोखिमों को कैसे संभाला जाए।

यह Linux/Unix IPC, एक फ़ाइल-डिस्क्रिप्टर टेबल और एक ओपन फ़ाइल डिस्क्रिप्शन के बीच के अंतर, और sendmsg/recvmsg के सहायक डेटा (ancillary-data) प्रोटोकॉल का परीक्षण करता है। Linux unix(7) प्रक्रियाओं के बीच ओपन फ़ाइल डिस्क्रिप्टर्स के एक सेट को भेजने या प्राप्त करने के लिए SCM_RIGHTS को परिभाषित करता है।

साक्षात्कारकर्ता क्या जांच रहा है

  • प्रोसेस-लोकल fd पूर्णांक (integer) और कर्नेल के ओपन फ़ाइल डिस्क्रिप्शन के बीच अंतर करना।
  • यह जानना कि SCM_RIGHTS सामान्य पेलोड में पूर्णांक रखने के बजाय सहायक डेटा का उपयोग करता है।
  • cmsghdr, CMSG_SPACE, और CMSG_LEN को सही ढंग से आकार देना, और MSG_CTRUNC की जांच करना।
  • सॉकेट-पाथ अनुमतियों, प्रेषक की पहचान, संसाधन सीमाओं, क्लोज़ टाइमिंग और विफलता क्लीनअप को कवर करना।

पहले स्पष्ट करने योग्य प्रश्न

  • क्या प्रक्रियाएं एक उपयोगकर्ता साझा करती हैं, और विशेषाधिकार डाउनग्रेड या ट्रस्ट सीमा कहाँ है?
  • क्या डिस्क्रिप्टर एक नियमित फ़ाइल, कनेक्टेड सॉकेट, लिसनिंग सॉकेट, epoll fd, या डिवाइस fd है?
  • क्या चैनल SOCK_STREAM है या SOCK_DGRAM, और क्या प्रोटोकॉल को संदेश सीमाओं और पावती (acknowledgements) की आवश्यकता है?
  • क्या रिसीवर प्रेषक क्रेडेंशियल्स, संसाधन प्रकार, केवल-पढ़ने योग्य (read-only) गुणों और अपेक्षित fd संख्या को सत्यापित कर सकता है?

30-सेकंड का उत्तर

मैं एक Unix-डोमेन सॉकेट जोड़ी बनाऊंगा, sendmsg से SOL_SOCKET/SCM_RIGHTS नियंत्रण संदेश में डिस्क्रिप्टर्स ले जाऊंगा, और वास्तविक पेलोड में प्रोटोकॉल संस्करण और अनुरोध आईडी डालूंगा। रिसीवर पर्याप्त बड़े CMSG_SPACE बफ़र के साथ recvmsg को कॉल करेगा, स्तर, प्रकार, लंबाई और MSG_CTRUNC की जांच करेगा, फिर प्राप्त fd को अपनी प्रक्रिया में एक नए पूर्णांक के रूप में उपयोग करेगा। जो सीमा पार करता है वह एक ओपन फ़ाइल डिस्क्रिप्शन का संदर्भ है, इसलिए रिसीवर को आमतौर पर एक अलग fd नंबर मिलता है। प्रोटोकॉल पीयर क्रेडेंशियल्स को सत्यापित करेगा, गिनती सीमित करेगा, close-on-exec सेट करेगा, और प्रत्येक त्रुटि पथ पर अस्वीकृत या अप्रयुक्त डिस्क्रिप्टर्स को बंद करेगा।

चरण-दर-चरण गहन विश्लेषण

fd संख्याओं को ओपन फ़ाइल डिस्क्रिप्शन्स से अलग करें

एक fd एक प्रक्रिया की fd तालिका में एक पूर्णांक सूचकांक (integer index) है। एक ओपन फ़ाइल डिस्क्रिप्शन एक कर्नेल ऑब्जेक्ट है जो फ़ाइल ऑफ़सेट और स्टेटस फ़्लैग जैसी खुली स्थिति रखता है। SCM_RIGHTS बाद वाले के संदर्भ की प्रतिलिपि बनाता है; रिसीवर को आमतौर पर एक अलग fd पूर्णांक मिलता है, जो शब्दार्थ के अनुसार किसी अन्य प्रक्रिया की fd तालिका में fd को डुप्लिकेट करने के समान है।

सामान्य बाइट्स के बजाय सहायक डेटा (ancillary data) का उपयोग करें

sendmsg और recvmsg msghdr.msg_control के माध्यम से cmsghdr रिकॉर्ड की एक श्रृंखला ले जाते हैं। cmsg_level को SOL_SOCKET पर सेट करें, cmsg_type को SCM_RIGHTS पर सेट करें, और डेटा क्षेत्र में एक पूर्णांक fd ऐरे रखें। सामान्य पेलोड संस्करण, उद्देश्य और एक पावती आईडी ले जा सकता है, लेकिन नियंत्रण संदेश को प्रतिस्थापित नहीं कर सकता।

नियंत्रण बफ़र को सही आकार दें

वास्तविक गिनती के लिए, प्रेषक cmsg_len के लिए CMSG_LEN(n * sizeof(int)) का उपयोग करता है; रिसीवर कम से कम CMSG_SPACE(n * sizeof(int)) का संरेखित स्थान आवंटित करता है। CMSG_FIRSTHDR और CMSG_NXTHDR के साथ पार्स करें, छोटी लंबाई और अप्रत्याशित प्रकारों को अस्वीकार करें।

ट्रंकेशन और स्ट्रीम सीमाओं को संभालें

यदि प्राप्त नियंत्रण बफ़र बहुत छोटा है, तो सहायक डेटा को छोटा या छोड़ दिया जा सकता है और MSG_CTRUNC सेट किया जाता है; रिसीवर को आंशिक डिस्क्रिप्टर सूची का उपयोग नहीं करना चाहिए। Linux को SOCK_STREAM पर सहायक डेटा के साथ कम से कम एक वास्तविक बाइट की आवश्यकता होती है, और सहायक डेटा एक प्राप्त बाधा (receive barrier) बनाता है, इसलिए बाइट-स्ट्रीम स्थिति पर भरोसा करने के बजाय नियंत्रण संदेश को अनुरोध आईडी से बांधें।

पहचान और ऑथराइजेशन सीमाएं स्थापित करें

डायरेक्टरी और सॉकेट अनुमतियां एक फ़ाइल सिस्टम सॉकेट के लिए पहली सीमा हैं। सर्वर को SO_PEERCRED या SCM_CREDENTIALS का भी उपयोग करना चाहिए और एप्लिकेशन लेयर पर किरायेदार (tenant), उद्देश्य और संसाधन प्रकार की पुष्टि करनी चाहिए। केवल एक fd प्राप्त करने से अतिरिक्त अधिकार नहीं मिलते हैं; प्रेषक को केवल एक अधिकृत संदर्भ स्थानांतरित करना चाहिए।

लाइफटाइम और संसाधन सीमाओं का प्रबंधन करें

प्रेषक भेजने के बाद अपना स्वयं का fd बंद कर सकता है, लेकिन कर्नेल तब तक एक इन-फ़्लाइट संदर्भ रखता है जब तक कि रिसीवर इसे स्वीकार नहीं कर लेता। Linux RLIMIT_NOFILE और SCM_MAX_FD के साथ ऑपरेशन को सीमित करता है; वर्तमान man पृष्ठ SCM_MAX_FD को आमतौर पर 253 के रूप में दर्ज करता है, जबकि पुराने संस्करण 255 का उपयोग करते थे। प्रति संदेश और प्रति वर्कर डिस्क्रिप्टर को सीमित करें, और अस्वीकृतियों को अवलोकनीय (observable) बनाएं।

c
struct msghdr msg = {0};
struct iovec iov = {.iov_base = "F", .iov_len = 1};
union { char buf[CMSG_SPACE(sizeof(int))]; struct cmsghdr align; } control;
msg.msg_iov = &iov; msg.msg_iovlen = 1;
msg.msg_control = control.buf; msg.msg_controllen = sizeof(control.buf);
struct cmsghdr *c = CMSG_FIRSTHDR(&msg);
c->cmsg_level = SOL_SOCKET; c->cmsg_type = SCM_RIGHTS;
c->cmsg_len = CMSG_LEN(sizeof(int));
memcpy(CMSG_DATA(c), &fd, sizeof(fd));
sendmsg(sock, &msg, MSG_NOSIGNAL);

एक मजबूत उत्तर का उदाहरण

मैं इसे पावती (acknowledgements) के साथ एक स्थानीय IPC प्रोटोकॉल बनाऊंगा। प्रेषक SOL_SOCKET/SCM_RIGHTS नियंत्रण संदेश के साथ एक Unix-डोमेन सॉकेट पर sendmsg का उपयोग करता है; वास्तविक पेलोड एक प्रोटोकॉल संस्करण, उद्देश्य और अनुरोध आईडी रखता है। रिसीवर CMSG_SPACE के साथ नियंत्रण स्थान आवंटित करता है, प्रकार, लंबाई और MSG_CTRUNC की जांच करता है, पीयर क्रेडेंशियल, डिस्क्रिप्टर गणना और संसाधन प्रकार की पुष्टि करता है, और उसके बाद ही इसे वर्कर को सौंपता है। महत्वपूर्ण सिमेंटिक बिंदु यह है कि स्थानांतरण एक ओपन फ़ाइल विवरण को संदर्भित करता है, इसलिए रिसीवर का पूर्णांक आमतौर पर भिन्न होता है और फ़ाइल ऑफ़सेट या ओपन स्थिति साझा की जा सकती है। मैं close-on-exec सेट करूँगा, इन-फ़्लाइट डिस्क्रिप्टर को सीमित करूँगा, RLIMIT_NOFILE और SCM_MAX_FD को संभालूँगा, और प्रत्येक विफलता पथ को बंद और ऑडिट करूँगा।

सामान्य गलतियां

  • fd पूर्णांक को JSON या बाइट पेलोड में लिखना और यह मान लेना कि अन्य प्रक्रिया इसका सीधे उपयोग कर सकती है।
  • CMSG_SPACE संरेखण की उपेक्षा करना और नियंत्रण डेटा के लिए केवल sizeof(int) आवंटित करना।
  • MSG_CTRUNC को छोड़ना और एक ऐसी डिस्क्रिप्टर सूची का उपयोग करना जिसे छोटा कर दिया गया था।
  • पीयर क्रेडेंशियल्स और उद्देश्य को सत्यापित किए बिना केवल Unix सॉकेट पथ की जांच करना।
  • यह भूल जाना कि रिसीवर को एक नया fd नंबर मिलता है या साझा ऑफ़सेट और स्थिति सिमेंटिक्स को निर्दिष्ट करने में विफल होना।
  • close-on-exec, fd सीमाओं, भेजने की विफलताओं और अप्रयुक्त डिस्क्रिप्टर्स को बंद करने की उपेक्षा करना।

फॉलो-अप और उत्तर

क्या रिसीवर को वही फ़ाइल डिस्क्रिप्टर मिलता है?

आमतौर पर समान पूर्णांक नहीं। कर्नेल रिसीवर की fd तालिका में उसी ओपन फ़ाइल विवरण के संदर्भ की प्रतिलिपि बनाता है, इसलिए फ़ाइल ऑफ़सेट और कुछ खुली स्थिति साझा की जा सकती है। यदि स्वतंत्र ऑफ़सेट की आवश्यकता है, तो समान fd संख्याओं को मानने के बजाय डेटा को फिर से खोलें या कॉपी करें।

एक वास्तविक बाइट क्यों भेजें?

Linux Unix स्ट्रीम सॉकेट्स को सहायक डेटा भेजे जाने पर उसी sendmsg में कम से कम एक वास्तविक बाइट की आवश्यकता होती है; यह प्रोटोकॉल को नियंत्रण संदेश को अनुरोध के साथ जोड़ने की भी अनुमति देता है। Linux डेटाग्राम इसे छोड़ सकते हैं, लेकिन पोर्टेबल कोड में अभी भी एक वास्तविक बाइट शामिल होना चाहिए।

यदि नियंत्रण बफ़र बहुत छोटा है तो क्या होगा?

सहायक डेटा को छोटा या छोड़ दिया जा सकता है और MSG_CTRUNC सेट किया जाता है। अमान्य या अतिरिक्त डिस्क्रिप्टर्स को बंद करें, प्रोटोकॉल त्रुटि लौटाएं, और घटना को रिकॉर्ड करें; कभी भी आंशिक सूची को पूर्ण प्राधिकरण के रूप में न मानें।

आप किसी विशेषाधिकार प्राप्त प्रक्रिया को गलत संसाधन सौंपने से कैसे रोकते हैं?

कनेक्शन को प्रतिबंधित करने के लिए सॉकेट अनुमतियों और पीयर क्रेडेंशियल्स का उपयोग करें, फिर एप्लिकेशन लेयर पर अनुरोध आईडी, किरायेदार, उद्देश्य और संसाधन प्रकार को बांधें। प्रेषक केवल एक अनुमत सूची (allowlist) से चयन करता है; रिसीवर केवल-पढ़ने योग्य गुणों, पथ या सॉकेट स्थिति की जांच करता है, और प्रत्येक प्राधिकरण और क्लोज़ का ऑडिट करता है।

सार्वजनिक स्रोत

संबंधित प्रश्न