اذهب إلى نمط go vet للعثور على استخدامات غير صحيحة لـ reflect.SliceHeader و reflect.StringHeader ، وقبول غير آمنة بين الهياكل ذات الحقول بحجم الهندسة المعمارية.

تقارير go-safer عن أنماط الاستخدام التالية:
reflect.SliceHeader reflect.StringHeaderstring النوع reflect.SliceHeader أو reflect.StringHeaderuint حيث تحتوي الهياكل على عدد مختلف من الحقول مع الأنواع ذات int المعتمد على العمارة uintptrيحدد النمط 1 الرمز الذي يشبه هذا:
func unsafeFunction ( s string ) [] byte {
sH := ( * reflect . StringHeader )( unsafe . Pointer ( & s ))
bH := & reflect. SliceHeader {
Data : sH . Data ,
Len : sH . Len ,
Cap : sH . Len ,
}
return * ( * [] byte )( unsafe . Pointer ( bH ))
} كما أنه سيحضر الحالات التي تم فيها إعادة تسمية reflect.SliceHeader ، كما هو الحال في type MysteryType reflect.SliceHeader .
يحدد النمط 2 رمزًا مثل ما يلي:
func unsafeFunction ( s string ) [] byte {
strH := ( * reflect . StringHeader )( unsafe . Pointer ( & str ))
sH := ( * reflect . SliceHeader )( unsafe . Pointer ( nil ))
sH . Len = strH . Len
sH . Cap = strH . Len
sH . Data = strH . Data
return
} ستقوم safer-go بالتقاط المهام إلى كائن من النوع reflect.SliceHeader . باستخدام الرسم البياني لتدفق التحكم في الوظيفة ، يمكن أن يرى أن sH لم يتم اشتقاقه عن طريق إلقاء شريحة حقيقية (هنا nil بدلاً من ذلك).
النمط 3 تم تحديد يلقي على النحو التالي:
type A struct {
x int
}
type B struct {
y int64
}
func unsafeFunction ( a A ) B {
return * ( * B )( unsafe . Pointer ( & a ))
} هناك المزيد من الأمثلة على غير صحيحة (تم الإبلاغ عنها) والرمز الآمن في حالات الاختبار في أدلة passes/*/testdata/src .
إذا لم يتم إنشاء reflect.SliceHeader أو reflect.StringHeader عن طريق إلقاء شريحة أو string حقيقية ، فلن يعامل وقت تشغيل GO حقل Data ضمن هذه الأنواع كمرجع إلى صفيف البيانات الأساسي. لذلك ، إذا كان جامع القمامة يعمل قبل الطاقم النهائي من مثيل الرأس الحرفي إلى شريحة أو string حقيقية ، فقد يجمع الشريحة أو string الأصلية. هذا يمكن أن يؤدي إلى تعرض تسرب المعلومات.
لمزيد من التفاصيل ، مثل استغلال إثبات المفهوم واقتراحًا لإصدار ثابت من هذه الأنماط غير الآمنة ، اقرأ منشور المدونة هذا: Golang Slice Header Conbusion GC على رمز العالم الحقيقي
لتثبيت go-safer ، استخدم الأمر التالي:
go get github.com/jlauinger/go-safer
سيؤدي ذلك إلى تثبيت go-safer على $GOPATH/bin ، لذا تأكد من تضمينه في متغير بيئة $PATH .
قم بتشغيل Go-Safer على حزمة مثل هذا:
$ go-safer example/cmd
أو توفير حزم متعددة ، مفصولة بالمسافات:
$ go-safer example/cmd example/util strings
للتحقق من حزمة ، بشكل متكرر ، كل وارداته ، استخدم ./... :
$ go-safer example/cmd/...
أخيرًا ، للتحقق من الحزمة في الدليل الحالي الذي يمكنك استخدامه . :
$ go-safer .
يقبل go-safer نفس الأعلام مثل go vet :
Flags:
-V print version and exit
-all
no effect (deprecated)
-c int
display offending line with this many lines of context (default -1)
-cpuprofile string
write CPU profile to this file
-debug string
debug flags, any subset of "fpstv"
-fix
apply all suggested fixes
-flags
print analyzer flags in JSON
-json
emit JSON output
-memprofile string
write memory profile to this file
-source
no effect (deprecated)
-tags string
no effect (deprecated)
-trace string
write trace log to this file
-v no effect (deprecated)
يطبع وضع علامة -help معلومات الاستخدام لـ go-safer :
$ go-safer -help
إذا كان مشروعك يستخدم وحدات GO وملف go.mod ، فسيحضر go-safer جميع التبعيات تلقائيًا قبل تحليلها. إنه يتصرف تمامًا مثل go build .
إذا كنت تستخدم شكلًا مختلفًا من إدارة التبعية ، فإن EG Manual go get ، أو go mod vendor أو أي شيء آخر ، فأنت بحاجة إلى تشغيل إدارة التبعية قبل تشغيل go-safer من أجل تحديث جميع التبعيات قبل التحليل.
للحصول على رمز المصدر وتجميع الثنائي ، قم بتشغيل هذا:
$ git clone https://github.com/jlauinger/go-safer
$ cd go-safer
$ go build
لتشغيل حالات الاختبار ، استخدم الأمر التالي:
$ go test ./...
يستخدم go-safer البنية التحتية للاختبار من golang.org/x/tools/go/analysis/analysistest . لإضافة حالة اختبار ، قم بإنشاء حزمة جديدة داخل الدلائل bad أو good في passes/sliceheader/testdata/src . أضف أكبر عدد ممكن من ملفات GO إلى الحزمة حسب الحاجة.
ثم ، قم بتسجيل الحزمة الجديدة في ملف sliceheader_test.go عن طريق تحديد مسار الحزمة.
في ملفات مصدر حالة الاختبار ، أضف تعليقات التعليقات التوضيحية إلى السطور التي يجب الإبلاغ عنها (أو لا). يجب أن تبدو التعليقات هكذا:
sH.Len = strH.Len // want "assigning to reflect header object" "assigning to reflect header object"
fmt.Println("hello world") // ok
يجب أن تبدأ التعليقات التوضيحية التي تشير إلى وجود خط يجب الإبلاغ عنه want ثم يكون للرسالة المطلوبة مرتين. لسبب ما ، ستؤدي البنية التحتية للاختبار إلى إخراج go-safer من التعليق التوضيحي مرتين ، وبالتالي يجب أن يتوقع مرتين أيضًا اجتياز الاختبار.
يمكن إضافة حالات اختبار تمثيل structcast بشكل مشابه.
نظرًا لأن go-safer مبني على البنية التحتية القياسية لـ Go Vet ، يمكنك استيراد تمريراتك إلى Linter المستندة إلى Vet.
مرخصة بموجب ترخيص معهد ماساتشوستس للتكنولوجيا ("الترخيص"). لا يجوز لك استخدام هذا المشروع إلا في الامتثال للترخيص. يمكنك الحصول على نسخة من الترخيص هنا.
حقوق الطبع والنشر 2020 يوهانس لاوينجر
تم تطوير هذه الأداة كجزء من أطروحة الماجستير في مجموعة تكنولوجيا البرمجيات في TU Darmstadt.