क्या यह असुरक्षित कोड .NET .NET 3 में भी काम करेगा?


42

Span<T>यदि संभव हो तो ढेर आवंटन से बचने के लिए मैं अपने पुस्तकालयों का उपयोग करने के लिए मना कर रहा हूं लेकिन जैसा कि मैंने भी पुराने ढांचे को लक्षित किया है, मैं कुछ सामान्य कमबैक समाधानों को भी लागू कर रहा हूं। लेकिन अब मुझे एक अजीब मुद्दा मिला और मुझे पूरा यकीन नहीं है कि मुझे .NET कोर 3 में एक बग मिला या मैं कुछ अवैध कर रहा हूं।

समस्या:

// This returns 1 as expected but cannot be used in older frameworks:
private static uint ReinterpretNew()
{
    Span<byte> bytes = stackalloc byte[4];
    bytes[0] = 1; // FillBytes(bytes);

    // returning bytes as uint:
    return Unsafe.As<byte, uint>(ref bytes.GetPinnableReference());
}

// This returns garbage in .NET Core 3.0 with release build:
private static unsafe uint ReinterpretOld()
{
    byte* bytes = stackalloc byte[4];
    bytes[0] = 1; // FillBytes(bytes);

    // returning bytes as uint:
    return *(uint*)bytes;
}

दिलचस्प रूप से पर्याप्त है, ReinterpretOld.NET फ्रेमवर्क और .NET कोर 2.0 में अच्छा काम करता है (इसलिए मैं इसके बाद खुश हो सकता हूं), फिर भी, यह मुझे थोड़ा परेशान करता है।

Btw। ReinterpretOldएक छोटे संशोधन द्वारा .NET कोर 3.0 में भी तय किया जा सकता है:

//return *(uint*)bytes;
uint* asUint = (uint*)bytes;
return *asUint;

मेरा प्रश्न:

क्या यह एक बग है या ReinterpretOldकेवल दुर्घटना से पुराने ढांचे में काम करता है और क्या मुझे उनके लिए भी फिक्स लागू करना चाहिए?

टिप्पणियों:

  • डीबग बिल्ड वर्क्स .NET .NET 3.0 में भी काम करता है
  • मैंने आवेदन [MethodImpl(MethodImplOptions.NoInlining)]करने की कोशिश की ReinterpretOldलेकिन इसका कोई असर नहीं हुआ।

2
FYI करें: return Unsafe.As<byte, uint>(ref bytes[0]);या return MemoryMarshal.Cast<byte, uint>(bytes)[0];- उपयोग करने की आवश्यकता नहीं है GetPinnableReference(); अन्य बिट में देख हालांकि
मार्क Gravell

SharpLab मामले में यह किसी और की मदद करता है। जो दो संस्करण Span<T>अलग-अलग आईएल को संकलित करने से बचते हैं। मुझे नहीं लगता कि आप कुछ भी अमान्य कर रहे हैं: मुझे एक JIT बग पर संदेह है।
छावनी 7

वह कचरा क्या है जो आप देख रहे हैं? आप स्थानीय लोगों को निष्क्रिय करने के लिए हैक का उपयोग कर रहे हैं? यह हैक काफी प्रभाव डालता है stackalloc(अर्थात यह आवंटित स्थान को मिटा नहीं सकता)
मार्क ग्रेवेल

@ canton7 अगर वे उसी IL में संकलित करते हैं, तो हम यह अनुमान नहीं लगा सकते हैं कि यह JIT बग है ... यदि IL समान है, आदि ... एक संकलक बग की तरह अधिक लगता है, अगर कुछ भी, शायद एक पुराने संकलक के साथ? György: क्या आप इंगित कर सकते हैं कि आप इसे कैसे संकलित कर रहे हैं? उदाहरण के लिए एसडीके क्या है? मैं कचरे को नहीं हटा सकता
मार्क ग्रेवेल

1
ऐसा लगता है कि स्टैकलोक हमेशा शून्य नहीं होता है, वास्तव में: लिंक
कैंटन 7

जवाबों:


35

ऊह, यह एक मजेदार खोज है; यहाँ क्या हो रहा है कि आपका स्थानीय अनुकूलित हो रहा है - कोई स्थानीय शेष नहीं है, जिसका अर्थ है कि कोई भी नहीं है .locals init, जिसका अर्थ है कि अलग तरीके से stackallocव्यवहार करता है , और अंतरिक्ष को मिटा नहीं करता है;

private static unsafe uint Reinterpret1()
{
    byte* bytes = stackalloc byte[4];
    bytes[0] = 1;

    return *(uint*)bytes;
}

private static unsafe uint Reinterpret2()
{
    byte* bytes = stackalloc byte[4];
    bytes[0] = 1;

    uint* asUint = (uint*)bytes;
    return *asUint;
}

हो जाता है:

.method private hidebysig static uint32 Reinterpret1() cil managed
{
    .maxstack 8
    L_0000: ldc.i4.4 
    L_0001: conv.u 
    L_0002: localloc 
    L_0004: dup 
    L_0005: ldc.i4.1 
    L_0006: stind.i1 
    L_0007: ldind.u4 
    L_0008: ret 
}

.method private hidebysig static uint32 Reinterpret2() cil managed
{
    .maxstack 3
    .locals init (
        [0] uint32* numPtr)
    L_0000: ldc.i4.4 
    L_0001: conv.u 
    L_0002: localloc 
    L_0004: dup 
    L_0005: ldc.i4.1 
    L_0006: stind.i1 
    L_0007: stloc.0 
    L_0008: ldloc.0 
    L_0009: ldind.u4 
    L_000a: ret 
}

मुझे लगता है कि मुझे यह कहते हुए खुशी होगी कि यह एक कंपाइलर बग है, या कम से कम: एक अवांछनीय साइड-इफ़ेक्ट और व्यवहार जिसे देखते हुए पिछले फैसले "लेट-इन-इनोकिट" , विशेष रूप से कोशिश करने के लिए कहें।stackalloc समझदार रखें - लेकिन क्या कंपाइलर लोग सहमत हैं या नहीं।

वर्कअराउंड यह है: stackallocअंतरिक्ष को अपरिभाषित मानें (जो कि निष्पक्ष होना है, वही है जो आप करने के लिए हैं); यदि आप इसे शून्य होने की उम्मीद करते हैं: मैन्युअल रूप से इसे शून्य करें।


2
ऐसा लगता है कि इसके लिए एक खुला टिकट है। मैं उस पर एक नई टिप्पणी जोड़ने जा रहा हूँ।
गियोगी कोज़ेग

हुह, मेरे सभी काम और मैंने ध्यान नहीं दिया कि पहला गायब था locals init। अच्छा है।
छावनी 7

1
@ canton7 यदि आप मेरे जैसे हैं कुछ भी, आप स्वचालित रूप से अतीत को छोड़ .maxstackऔर .localsनहीं करने के लिए नोटिस, जिससे यह विशेष रूप से आसान है कि यह है कि / नहीं वहाँ :)
मार्क Gravell

1
The content of the newly allocated memory is undefined.MSDN के अनुसार। विनिर्देश यह नहीं कहता कि मेमोरी को या तो शून्य किया जाना चाहिए। तो ऐसा लगता है कि यह केवल दुर्घटना से पुराने ढांचे पर काम करता है, या गैर-संविदात्मक व्यवहार के परिणामस्वरूप।
लुआएन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.